Activitats

Càrrega d'arxius amb Spring MVC

L’objectiu d’aquesta activitat és que l’estudiant conegui com es fa la una càrrega de fitxers al servidor web amb el tipus de petició multi-part (multipart request). Spring MVC proporciona tota la funcionalitat necessària per gestionar aquests tipus de petició.

Partint de l’aplicació Estoc de medicaments, en l’estat del final d’aquesta unitat, és a dir, del projecte “stmedioc404”, heu d’afegir la càrrega del prospecte del medicament, per exemple en PDF, al servidor.

El codi de la solució d’aquesta activitat el podeu descarregar del següent

enllaç ( 13.4 MB )
.

No obstant això, es recomana desenvolupar el projecte pas a pas, com es detalla a continuació.

Amb NetBeans, copieu el projecte “stmedioc404” amb el nom “carregafitxer”. El projecte “stmedioc404” el podeu descarregar del següent

enllaç ( 13.1 MB )
.

Afegint la configuració i les dependències

Creeu la carpeta /WEB-INF/resources, i a dins afegiu-hi la carpeta prospect.

Per tal de que Maven creï aquestes carpetes al target del projecte, quan es faci la construcció del mateix afegiu un fitxer .txt qualsevol a aquesta carpeta.

A DispatcherServlet-context.xml s’ha de configurar el bean que manegarà peticions que portin fitxers. Farem servir la classe CommonsMultipartResolver, que determinarà si la petició porta contingut multi-part (un fitxer), i en aquest cas agafarà el control. Per això, vegeu que també es determina la mida màxima del fitxer a pujar, entre altres personalitzacions que es poden fer.

Heu d’afegir el següent bean a DispatcherServlet-context.xml:

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="10240000"/>
    </bean>

El valor de maxUploadSize és en bytes.

Heu d’afegir dependències d‘org.apache.commons, perquè Spring fa servir aquestes llibreries per proporcionar la funcionalitat de càrrega de fitxers.

Afegiu les següents dependències a pom.xml:

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>

A més, si heu copiat el projecte, també a pom.xml canvieu el nom final del projecte.

    <build>        
        <finalName>carregafitxer</finalName>
    </build>

Adaptant el model

El domain object l’adaptarem per fer una referència al prospecte des de Medicament.

A la capa de domini, afegiu la següent propietat a Medicament (amb els getter i setter corresponents).

private MultipartFile medicamentProspect;

Adaptant la capa de presentació

Al controlador específic MedicamentController heu de substituir el mètode processAddNewMedicamentForm pel codi que es mostra.

    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String processAddNewMedicamentForm(@ModelAttribute("newMedicament") Medicament newMedicamentToAdd, BindingResult result, HttpServletRequest request) {
        String[] suppressedFields = result.getSuppressedFields();
        if (suppressedFields.length > 0) {
            throw new RuntimeException("Intentat accedir amb camps no permesos: " + StringUtils.arrayToCommaDelimitedString(suppressedFields));
        }
        MultipartFile medicamentProspect = newMedicamentToAdd.getMedicamentProspect();
        String rootDirectory = request.getSession().getServletContext().getRealPath("/");
        if (medicamentProspect != null && !medicamentProspect.isEmpty()) {
            try {
                medicamentProspect.transferTo(new File(rootDirectory + "resources\prospect\" + newMedicamentToAdd.getMedicamentId() + ".pdf"));
            } catch (Exception e) {
                throw new RuntimeException("El prospecte no s'ha pogut pujar ", e);
            }
        }
        medicamentService.addMedicament(newMedicamentToAdd);
        return "redirect:/medicaments/all";
    }

Hem modificat els paràmetres del mètode afegint HttpServletRequest request i Hem fet servir MultipartFile de Spring per fer efectiva la transferència del fitxer.

A la vista addMedicament.jsp heu de modificar diverses parts del formulari. A la definició del formulari (etiqueta form:form), afegiu el següent atribut, que farà que la petició sigui multi-part.

 enctype="multipart/form-data"

Després del control actiu (Si, No), afegiu el control per a la càrrega de fitxers amb el codi següent:

                    <div class="form-group">
                        <label class="control-label col-lg-2" for="medicamentProspect">
                            <spring:message code="addProdcut.form.medicamentProspect.label"/></label>
                        <div class="col-lg-10">
                            <form:input id="medicamentProspect" path="medicamentProspect" type="file" class="form:input-large" />
                        </div>
                    </div>

Afegiu el text de l’etiqueta als fitxers messages_xx.properties que calgui per mostrar l’etiqueta en els idiomes configurats. Per exemple:

addProdcut.form.medicamentProspect.label = Fitxer del prospecte

Provant la càrrega de fitxers

Executeu l’aplicació i aneu a la pàgina localhost:8080/carregafitxer/medicaments/add (és possible que us hagueu d’identificar).

Un cop autenticats ja podeu omplir el formulari i carregar el fitxer. Per comprovar que s’ha pujat, aneu directament al sistema de fitxers i dins del projecte aneu a la carpeta /target/carregafitxer/WEB-INF/resources/prospect, on trobareu el fitxer pujat.

Respostes XML i Json amb Spring MVC

L’objectiu d’aquesta activitat és donar alternatives a l’estudiant afegint la possibilitat de retornar vistes XML o JSON a més de JSP.

Partint de l’aplicació Estoc de medicaments en l’estat del final d’aquesta unitat, és a dir, del projecte “stmedioc404”, mostreu la pàgina de detall de medicament amb XML i amb JSON.

Farem servir dues tècniques per proporcionar vistes en format XML i JSON, i veurem que ambdues són vàlides, encara que són lleugerament diferents.

En primer lloc, veureu un exemple amb l’anotació @ResponseBody que posarem com a tipus de resposta al mètode que recollirà la petició. Veureu que construïu la resposta com una llista de domain objects directament, en comptes d’un ModelAndView.

En segon lloc, fareu servir ContentNegotiatingViewResolver, que us permetrà incorporar vistes com MappingJacksonJsonView per a JSON i MarshallingView per a XML. En aquest cas, el mètode que les recollirà és el normal, és a dir, la configuració farà que sàpiga el format en el qual ha de construir la resposta en funció de l’URL de la petició.

El codi de la solució d’aquesta activitat el podeu descarregar del següent

enllaç ( 17.3 MB )
.

No obstant això, es recomana desenvolupar el projecte pas a pas, com es detalla a continuació.

Amb NetBeans, copieu el projecte “stmedioc404” amb el nom “ajaxxmljson”. El projecte “stmedioc404” el podeu descarregar al següent

enllaç ( 13.1 MB )
.

CAS A. AMB L’ANOTACIÓ @ResponseBody

Afegint la configuració i les dependències

En aquest cas no calen dependències ni configuracions addicionals. Spring MVC, de manera nativa, permet la construcció de respostes amb XML o amb JSON.

Adaptant el model

El domain object l’adaptarem per permetre la generació de XML amb diverses anotacions. En aquest cas, no afegirem més configuració a la resposta JSON.

A la capa de domini, a Medicament, afegiu l’anotació @XmlRootElement a nivell de la classe.

@XmlRootElement
public class Medicament {

Adaptant el controlador

En aquest cas s’han de diferenciar les peticions XML i JSON de les normals (/medicament), atès que el mètode getMedicamentById que recull aquestes peticions no les sabrà diferenciar i sempre mostrarà la vista jsp.

Afegiu el mètode getMedicamentByIdRB al controlador getMedicamentByIdRB amb el codi i l’anotació que es mostra a continuació.

    @RequestMapping(value="/medicament/responsebody")
    public @ResponseBody Medicament getMedicamentByIdRB(@RequestParam("codi") String medicamentId, HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        return medicamentService.getMedicamentById(medicamentId);

    }

Provant l’aplicació

Si executeu la petició normal, localhost:8080/ajaxxmljson/medicaments/medicament?codi=M020 us ha de mostrar el detall de medicament amb la vista jsp ja coneguda.

Figura Detall de medicament. Sortida .jsp

En canvi, podeu obtenir les mateixes dades en format JSON amb l’URL localhost:8080/ajaxxmljson/medicaments/medicament/responsebody.json?codi=M020.

Figura Detall de medicament. Sortida JSON

I també en XML amb l’URL localhost:8080/ajaxxmljson/medicaments/medicament/responsebody.xml?codi=M020.

Figura Detall de medicament. Sortida XML

Amb l’URL /medicament sense més, agafa el control el mètode ja desenvolupat i, per tant, mostra el detall en jsp. En canvi, amb l’URL /medicament/responsebody.xxx el control el pren el mètode public @ResponseBody Medicament getMedicamentByIdRB.

En aquest mètode es construeix una llista de domain objects i, com que no es pot construir una view amb una List, Spring MVC, a partir de @ResponseBody i l’extensió xxx de l’URL, la converteix en el format adient (XML per defecte o JSON).

CAS B. AMB L’OBJECTE ContentNegotiatingViewResolver

En aquest cas volem aconseguir que, amb l’URL inicial /medicament i afegint l’extensió (cap extensió, .json, .xml), Spring MVC sàpiga retornar la vista en format adient.

Afegint la configuració i les dependències

Al fitxer dependències pom.xml hem d’afegir la dependència que es mostra a continuació, perquè baixarà les llibreries necessàries per generar JSON. Per a XML no calen dependències addicionals.

        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>1.9.10</version>
        </dependency>

A DispatcherServlet-context.xml s’ha de configurar el bean ContentNegotiatingViewResolver, que ens permetrà compondre i retornar les vistes XML i JSON.

Heu d’afegir el següent bean a DispatcherServlet-context.xml:

    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="mediaTypes">
            <map>
                <entry key="json" value="application/json" />
                <entry key="xml" value="application/xml" />
            </map>
        </property>
        <property name="defaultViews">
            <list>
                <ref bean="jsonView"/>
                <ref bean="xmlView"/>              
            </list>
        </property>
    </bean>

Fixeu-vos que fem referència a jsonView i xmlView. Per tant, afegiu aquests beans amb les etiquetes que es mostren a continuació.

    <bean id="jsonView" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
        <property name="prettyPrint" value="true"/>
    </bean>
    <bean id="xmlView" class="org.springframework.web.servlet.view.xml.MarshallingView">
        <constructor-arg>
            <bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
                <property name="classesToBeBound">
                    <list>
                        <value>cat.xtec.ioc.domain.Medicament</value>
                    </list>
                </property>
            </bean>
        </constructor-arg>
    </bean>   

Adaptant el model

Per a XML no necessitem més adaptacions, atès que ja hem afegit l’anotació @XmlRootElement a la classe Medicament.

En canvi, simplement a mode d’exemple, ara que tenim la nova dependència podem fer servir alguna de les anotacions que ens proporciona. En aquest cas farem servir l’anotació @JsonIgnore per no generar la propietat active quan la resposta és JSON.

A la capa de domini, a Medicament, afegiu l’anotació @JsonIgnore a nivell de la propietat active.

    @JsonIgnore
    private boolean active;

Adaptant el controlador

Al controlador no s’ha de fer res, ja que davant d’una petició /medicament actuarà el bean ContentNegotiatingViewResolver, que decidirà quin bean ha de construir la resposta.

Provant l’aplicació

Si executeu la petició normal, localhost:8080/ajaxxmljson/medicaments/medicament?codi=M020 us ha de mostrar el detall de medicament amb la vista jsp ja coneguda.

Figura Detall de medicament. Sortida .jsp

En canvi, podeu obtenir les mateixes dades en format JSON amb l’URL localhost:8080/ajaxxmljson/medicaments/medicament.json?codi=M020.

Figura Detall de medicament. Sortida JSON

I també en XML amb l’URL localhost:8080/ajaxxmljson/medicaments/medicament.xml?codi=M020.

Figura Detall de medicament. Sortida XML

Amb l’URL /medicament sense més, ContentNegotiatingViewResolver no el considera perquè no està a la seva llista de vistes a gestionar. Per tant, es mostra la resposta en el format per defecte jsp.

En els altres casos entra en joc ContentNegotiatingViewResolver i retorna la vista adient segons la petició sigui JSON o XML.

Anar a la pàgina anterior:
Spring MVC, altres aplicacions
Anar a la pàgina següent:
Exercicis d'autoavaluació