Spring MVC, aprofundint en els controladors

Partiu de l’aplicació web Estoc de medicaments que mostra una pàgina de benvinguda, i que mitjançant l’URL adient pot mostrar una llista de medicaments i fins i tot rebaixar l’estoc d’un d’aquests.

Aquesta aplicació segueix l’arquitectura i el procés de qualsevol aplicació Spring MVC. L’aplicació s’estructura en les quatre capes següents:

  • presentació (Presentation)
  • domini (Domain)
  • servei (Service)
  • persistència (Persistence)

Estoc de medicaments fa servir anotacions de Spring MVC:

  • @Controller, @Repository i @Service, que defineixen el tipus d’objecte Spring.
  • @RequestMapping, que caça l’URL definit i executa el mètode que el segueix.
  • @Autowired, que injecta l’objecte que segueix.

A més, Estoc de medicaments conté els fitxers XML amb la configuració necessària per dirigir el comportament de Spring MVC respecte a l’aplicació.

Anem a afegir més funcionalitat a l’aplicació Estoc de medicaments per conèixer els conceptes i procediments associats, aprofundint un punt més en l’estudi dels controladors.

Estoc de medicaments mostra la llista dels medicaments. El procés per mostrar la llista el canviareu per adaptar-lo a un veritable procés Spring MVC, és a dir, creant el servei corresponent i fent que el controlador cridi el servei en comptes del que fa ara, cridar directament la persistència. Aprofitant aquesta implementació, usareu l’anotació @RequestMapping a nivell de classe i a nivell de mètode.

Fareu que Estoc de medicaments mostri medicaments d’una categoria, però en aquest cas introduireu com l’URL de @RequestMapping pot ser variable (path variable) afegint l’anotació @PathVariable. Ja us convé, atès que ara, pel moviment d’estoc, l’URL és fix.

Aprofundint en variables a l’URL permetreu que Estoc de medicaments mostri medicaments basats en un filtre, és a dir, en un conjunt de parells (propietat, valor) que implementareu amb l’anotació @MatrixVariable i una col·lecció Map (matrix variable).

Finalment, l’aplicació podrà mostrar el detall d’un medicament. En aquest cas no fareu servir variables a l’URL, sinó que utilitzareu l’anotació @RequestParam per definir paràmetres HTTP a partir de peticions Get o Post.

Estoc de medicaments, capa de servei a medicament

En l’aplicació Estoc de medicaments de la qual partiu, el controlador MedicamentController crida directament la capa de persistència; concretament, es fa un injecció de MedicamentRepository.

Arregleu això implementant la capa de servei corresponent i aprofiteu per endreçar una mica les crides fent que mostrar tots els medicaments sigui un cas particular de mostrar medicaments.

El codi del projecte “stmedioc” en l’estat capa de servei a medicament es pot descarregar des de l’enllaç que trobareu als annexos de la unitat.

Però per seguir el desenvolupament que segueix és millor partir del que ja heu fet servir i copiar-lo amb el nom de “stmedioc301”.

Creant el servei a medicament

Al paquet cat.xtec.ioc.service heu de crear la interfície MedicamentService amb el codi que es mostra a continuació:

package cat.xtec.ioc.service;

import cat.xtec.ioc.domain.Medicament;
import java.util.List;

public interface MedicamentService {

    List<Medicament> getAllMedicaments();

    Medicament getMedicamentById(String medicamentID);
}

L’objectiu és mostrar la llista de medicaments amb les crides ortodoxes (presentació a servei i aquest a persistència), i per això definiu getAllMedicaments. També aprofiteu per endreçar i afegiu el mètode getMedicamentById, que fareu servir quan vulgueu un medicament a partir del seu codi.

Al paquet cat.xtec.ioc.service.impl heu de crear la classe MedicamentServiceImpl que implementi la interfície anterior amb el codi que es mostra.

package cat.xtec.ioc.service.impl;

import cat.xtec.ioc.domain.Medicament;
import cat.xtec.ioc.domain.repository.MedicamentRepository;
import cat.xtec.ioc.service.MedicamentService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MedicamentServiceImpl implements MedicamentService {

    @Autowired
    private MedicamentRepository medicamentRepository;

    public List<Medicament> getAllMedicaments() {
        return medicamentRepository.getAllMedicaments();
    }

    public Medicament getMedicamentById(String medicamentID) {
        return medicamentRepository.getMedicamentById(medicamentID);
    }
}

En aquest cas, el servei no afegeix més regles de negoci, simplement es limita a cridar la persistència per obtenir el resultat. No obstant això, s’ha d’implementar aquesta capa perquè en el futur és possible que sorgeixin necessitats a implementar en aquesta capa. Per exemple, podríem dir que encara que obtingueu un medicament el retornareu només si l’usuari està autoritzat a la seva categoria.

Ordenant les crides des del controlador

Canvieu tot el controlador MedicamentController amb el codi que es mostra a continuació:

package cat.xtec.ioc.controller;

import cat.xtec.ioc.service.MedicamentService;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/medicaments")
public class MedicamentController {

    @Autowired
    private MedicamentService medicamentService;

    @RequestMapping("/all")
    public ModelAndView allMedicaments(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        ModelAndView modelview = new ModelAndView("medicaments");
        modelview.getModelMap().addAttribute("medicaments", medicamentService.getAllMedicaments());
        return modelview;
    }
}

Heu canviat la injecció del repository per la del nou servei. Ara sou a prop de l’ortodòxia a Spring MVC.

L’anotació @RequestMapping permet al Servlet Dispatcher encaminar la petició sobre el controlador específic que ha de processar-la.

Ara teniu dos @RequestMapping: un a nivell de classe amb l’URL /medicaments i un altre a nivell del mètode allMedicaments amb l’URL /all.

Si convenim que l’URL inicial de l’aplicació és urlbase (p. ex.: localhost/stmedioc301), quan el Dispatcher Servlet trobi l’URL urlbase/medicaments/all, amb /medicaments determinarà que el controlador que ha de gestionar la petició és MedicamentController, i amb /all el mètode a executar serà allMedicaments.

D’aquesta manera, dins de MedicamentController podríeu afegir més mètodes que agafin el control quan es doni una petició urlbase/medicaments/xxx, on xxx és l’URL que posaríeu a l’anotació @RequestMapping a nivell del mètode corresponent.

Proveu a executar l’aplicació afegint /medicaments/all a l’URL, i us ha de mostrar la llista de medicaments.

Estoc de medicaments, medicaments per malaltia

Voleu que l’aplicació Estoc de medicaments mostri la llista de medicaments d’una categoria. Pel que sabeu, podeu fer els següents passos d’implementació:

  • Un mètode del repositori que doni aquesta llista de medicaments a partir de la categoria donada com a paràmetre.
  • Un mètode al servei que cridi aquest mètode del repositori.
  • Un mètode al controlador que caci l’URL de la categoria i cridi el servei.

Però amb això, l’anotació @RequestMapping al controlador seria fixa amb la categoria com a constant, com per exemple …/medicaments/Analgèsic. Amb dues categories, com és el cas de la vostra aplicació, encara es pot suportar, però si en teniu algunes més ja no tindria sentit, ja que hauríeu de posar un @RequestMapping amb cada categoria com a URL. Encara més, no sabeu quines categories es poden crear en el futur, i per en cada una hauríeu de modificar el codi.

No us heu de preocupar, perquè podeu fer servir la funcionalitat de Spring MVC anomenada plantilla d’URI (URI template pattern).

En el cas d’Estoc de medicaments, teniu:

  • localhost:8080/stmedioc301/medicaments/Analgèsic
  • localhost:8080/stmedioc301/medicaments/Anti-inflamatori

on podríeu dir que la darrera part de l’URL és variable.

A Spring MVC, aquesta variable es nota entre claus i és una variable de ruta (path variable). Si l’anomeneu categoria, llavors l’URL la podríeu considerar com localhost:8080/stmedioc301/medicaments/{categoria}.

Amb aquesta definició ja podeu començar a implementar la solució per mostrar els medicaments per malaltia amb una variable de ruta.

El codi del projecte “stmedioc” en l’estat medicaments per malaltia es pot descarregar des de l’enllaç que trobareu als annexos de la unitat.

Però per seguir el desenvolupament és millor partir del que ja heu fet servir i copiar-lo amb el nom de “stmedioc301”.

Adaptant repositori i servei

A la interfície MedicamentRepository, afegiu la següent declaració de mètode:

List<Medicament> getMedicamentsByCategory(String category);

I a la classe InMemoryMedicamentRepository, implementeu el mètode amb el codi que es mostra:

    public List<Medicament> getMedicamentsByCategory(String category) {
        List<Medicament> medicamentsByCategory = new ArrayList<Medicament>();
        for (Medicament medicament : listOfMedicaments) {
            if (category.equalsIgnoreCase(medicament.getCategory())) {
                medicamentsByCategory.add(medicament);
            }
        }
        return medicamentsByCategory;
    }

El mètode getMedicamentsByCategory retornarà una List amb objectes Medicament de la categoria passada per paràmetre. Fixeu-vos que ignora diferències entre majúscules i minúscules.

A la interfície MedicamentService, afegiu la següent declaració de mètode:

List<Medicament> getMedicamentsByCategory(String category);

I a la classe MedicamentServiceImpl, implementeu el mètode amb el codi que es mostra:

    public List<Medicament> getMedicamentsByCategory(String category) {
        return medicamentRepository.getMedicamentsByCategory(category);
    }

Simplement, es crida el repositori sense més regles de negoci a afegir.

Adaptant el controlador

A MedicamentController, afegireu l’anotació @RequestMapping per caçar qualsevol categoria, és a dir, fareu servir una variable de ruta (path variable). I al mètode que segueix, recuperareu aquesta variable per passar-la com a paràmetre en la cerca de medicaments per categoria.

A la classe MedicamentController, afegiu l’anotació i el mètode amb el codi que es mostra:

    @RequestMapping("/{category}")
    public ModelAndView getMedicamentsByCategory(@PathVariable("category") String medicamentCategory, HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        ModelAndView modelview = new ModelAndView("medicaments");
        modelview.getModelMap().addAttribute("medicaments", medicamentService.getMedicamentsByCategory(medicamentCategory));
        return modelview;
    }

Us demanarà afegir l’import següent:

import org.springframework.web.bind.annotation.PathVariable;

A @RequestMapping feu servir l’anotació {pathVariable} per indicar a Spring que l’URL contindrà un valor variable en aquesta part. En el vostre cas, el nom de la variable de ruta és category.

El mètode que es llança és getMedicamentsByCategory, i en els seus paràmetres heu fet servir l’anotació @PathVariable amb el format:

  @PathVariable(pathVariable) type variableMetode

on pathVariable és el nom de la variable de ruta definida a l’anotació @RequestMapping, variableMetode és la variable que fareu servir al mètode, i type és el seu tipus. Així, Spring guarda el valor que s’ha posat a l’URL a variableMetode i ja la podeu fer servir amb aquest nom en el codi inclòs en el mètode.

@PathVariable sense valors de paràmetre implica que pathVariable té el mateix nom que variableMetode.

En el vostre cas, si executeu l’aplicació per exemple amb l’URL localhost:8080/stmedioc302/medicaments/Analgèsic, el DispatcherServlet, com que l’URL és urlbase/medicaments/…, passarà el control a MedicamentController. Dins d’aquest controlador es crida el mètode precedit per l’anotació @RequestMapping(”/{category}”), ja que s’ha passat un URL del tipus urlbase/medicaments/xxx, on xxx és qualsevol valor diferent d’all (existeix @RequestMapping(”/all”), que cridaria allMedicaments).

El mètode getMedicamentsByCategory cridat demana al repositori els medicaments de la categoria que s’ha passat per paràmetre i mostra la vista de medicaments només amb els que ha trobat.

Així, el resultat per a l’URL passat hauria de ser el que es mostra en la figura.

Figura Sortida de medicaments per categoria

"Estoc de medicaments", llista de medicaments segons filtre

“Estoc de medicaments” pot mostrar medicaments d’una categoria passant l’URL amb /medicaments/nomDeCategoria, on nomDeCategoria serà rebut al controlador específic amb l’anotació @RequestMapping(”/{category}”) que conté la variable de ruta (path variable).

Els vostres requeriments es veuen ampliats amb la necessitat de mostrar medicaments segons filtres. Enriquireu l’aplicació acceptant URL del tipus localhost:8080/stmedioc303/medicaments/filter/ByCriteria;producer=Ferrer,Bayer;es toc=1,100, que, per exemple, podeu traduir com a llista de medicaments dels productors Ferrer o Bayer amb unitats en estoc entre 1 i 100. Però com veureu, la traducció és un conveni, perquè depèn del que implementeu a la consulta que fareu a les dades.

En realitat, el que voleu implementar és l’acceptació de variables de matriu (matrix variables) com a paràmetres i com fer-les servir al vostre codi.

Aquestes variables es convertiran en un Map<String,List<String» a partir de l’anotació @MatrixVariable. Fixeu-vos que té sentit, perquè producer=Ferrer,Bayer;estoc=1,100 es pot representar com una Map amb les keys producer i estoc, i els valors són llistes.

Implementeu l’exemple que dóna resposta a localhost:8080/stmedioc303 /medicaments/filter/ByCriteria;producer=Ferrer,Bayer;estoc=1,100 amb el significat que hem convingut.

El codi del projecte “stmedioc” en l’estat llista de medicaments segons filtre es pot descarregar des de l’enllaç que trobareu als annexos de la unitat.

Però per seguir el desenvolupament és millor partir del que ja heu fet servir i copiar-lo amb el nom de “stmedioc303”.

Adaptant repositori i servei

A la interfície MedicamentRepository, afegiu la següent declaració de mètode:

    Set<Medicament> getMedicamentsByFilter(Map<String, List<String>> filterParams);

I a la classe InMemoryMedicamentRepository, implementeu el mètode amb el codi que es mostra.

    public Set<Medicament> getMedicamentsByFilter(Map<String, List<String>> filterParams) {
        Set<Medicament> medicamentsByProducer = new HashSet<Medicament>();
        Set<Medicament> medicamentsInStockRange = new HashSet<Medicament>();
        Set<String> criterias = filterParams.keySet();
        long minStock = 0;
        long maxStock = 0;
        if (criterias.contains("producer")) {
            for (String producerName : filterParams.get("producer")) {
                for (Medicament medicament : listOfMedicaments) {
                    if (producerName.equalsIgnoreCase(medicament.getProducer())) {
                        medicamentsByProducer.add(medicament);
                    }
                }
            }
        }
        if (criterias.contains("estoc")) {
            minStock = Long.parseLong(filterParams.get("estoc").get(0));
            maxStock = Long.parseLong(filterParams.get("estoc").get(1));

            for (Medicament medicament : listOfMedicaments) {
                if ((medicament.getStockQuantity() >= minStock) && (medicament.getStockQuantity() <= maxStock)) {
                    medicamentsInStockRange.add(medicament);
                }
            }

        }
        medicamentsInStockRange.retainAll(medicamentsByProducer);
        return medicamentsInStockRange;
    }

Set és una interfície del framework Java Collections. Es pot consultar la referència a la mateixa documentació d’Oracle a bit.ly/2r3n02Y.

Es defineixen dues col·leccions (medicamentsByProducer i medicamentsInStockRange) que contindran els medicaments que compleixen els criteris per retornar només una amb la intersecció d’elements.

És defineix un Set de criteris que en el vostre cas contindrà producer i estoc. Fixeu-vos que podreu provar sense algun dels criteris, i llavors aquest no es tindrà en compte.

En el cas del criteri producer, se seleccionen els elements del paràmetre amb aquest criteri, es recorre aquesta selecció i per a cada valor s’afegeixen els medicaments amb aquest producer al Set medicamentsByProducer.

En el cas del criteri estoc, es fan servir les variables minStock i maxStock per assignar respectivament els elements 0 i 1 de la llista de paràmetres amb la key estoc (filterParams.get(“estoc”).get(i)) Llavors, es van afegint sobre el Set medicamentsInStockRange els medicaments amb unitats en estoc en el rang [minStock,maxStock].

Finalment, com que voleu retornar la llista de medicaments que compleixen tots els criteris a la vegada, agafeu una de les llistes i només la deixeu amb els medicaments que coincideixen amb l’altre, per finalment retornar la intersecció.

medicamentsInStockRange.retainAll(medicamentsByProducer);

Un cop heu definit la consulta a les dades, passeu a modificar la capa de servei.

A la interfície MedicamentService, afegiu la següent declaració de mètode:

       Set<Medicament> getMedicamentsByFilter(Map<String, List<String>> filterParams);

I a la classe MedicamentServiceImpl, implementeu el mètode amb el codi que es mostra:

    public Set<Medicament> getMedicamentsByFilter(Map<String, List<String>> filterParams) {
        return medicamentRepository.getMedicamentsByFilter(filterParams);
    }

Simplement es crida el repositori, sense més regles de negoci a afegir.

Adaptant el controlador

Al controlador específic voleu fer servir variables tipus matriu, com per exemple les que es mostren a l’URL localhost:8080 /stmedioc303/medicaments/filter/ByCriteria;producer=Ferrer,Bayer;estoc=1,100.

Tal com heu implementat al mètode getMedicamentsByFilter del repositori, heu convingut que això voldrà dir el conjunt de medicaments amb productor Bayer o Ferrer, i a més l’estoc dels quals estigui entre 1 i 100 unitats.

Per aconseguir això es farà servir l’anotació @MatrixVariable, però, a més, a Spring MVC es necessita una configuració addicional.

A DispatcherServlet-servlet.xml canvieu l’etiqueta mvc:annotation-driven així:

<mvc:annotation-driven enable-matrix-variables="true"/>

A la classe MedicamentController, afegiu l’anotació i el mètode amb el codi que es mostra:

    @RequestMapping("/filter/{ByCriteria}")
    public ModelAndView getMedicamentsByFilter(@MatrixVariable(pathVar = "ByCriteria") Map<String, List<String>> filterParams, HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        ModelAndView modelview = new ModelAndView("medicaments");
        modelview.getModelMap().addAttribute("medicaments",    medicamentService.getMedicamentsByFilter(filterParams));
        return modelview;
    }

A @RequestMapping feu servir l’anotació {pathVariable} per indicar a Spring que l’URL contindrà un valor variable en aquesta part. En el vostre cas, el nom de la variable és ByCriteria.

El mètode que es llança és getMedicamentsByFilter, i en els seus paràmetres hem fet servir l’anotació @MatrixVariable amb el format:

@MatrixVariable(pathVar = "pathVariable") Map<String, List<String>> filterParams

on pathVariable és el nom de la variable definida a l’anotació @RequestMapping i filterParams és la variable que farem servir al mètode amb tipus Map. Així, Spring guarda el valor que s’ha posat a l’URL a filterParams i ja la podeu fer servir amb aquest nom en el codi inclòs en el mètode.

L’anotació @RequestMapping permet definir més d’un grup de criteris, és a dir, més d’un grup de matrix variables. El format seria així: @RequestMapping( ”/filter/{ByCriteria}/ {ByOtherCriteria}”).

En el vostre cas, si executeu l’aplicació per exemple amb l’URL localhost:8080/stmedioc303/medicaments/filter/ByCriteria;producer=Ferrer,Bayer;es toc=1,100, com que l’URL és urlbase/medicaments/…, el DispatcherServlet passarà el control a MedicamentController. Dins d’aquest controlador es crida el mètode precedit per l’anotació @RequestMapping(”/filter/{ByCriteria}”), ja que s’ha passat un URL del tipus urlbase/medicaments/filter/ByCriteria;xxx, on xxx és un conjunt de parells clau=llista de valors.

El mètode getMedicamentsByFilter cridat demana al repositori els medicaments amb el filtre que s’ha passat per paràmetre i mostra la vista medicaments només amb els que ha trobat. Fixeu-vos que el paràmetre “filtre”, conjunt de parells clau=llista de valors, s’ha convertit a un Map<String, List<String>> que anomenem filterParams, i amb aquest nom el fem servir al codi del mètode.

Així, el resultat per a l’URL passat hauria de ser el que es mostra en la figura.

Figura Sortida de la llista de medicaments amb filtre

Podeu provar URL amb altres rangs d’estoc i altres productors.

"Estoc de medicaments", detall de medicament

“Estoc de medicaments” mostra llistes de medicaments que compleixen certs criteris basats en variables que posem a l’URL. És a dir, en els casos que es gestionen amb les anotacions @PathVariable i @MatrixVariable, s’estan rebent els valors com a part de l’URL i no com a variables que formen part d’ HTTP request.

Amplieu l’aplicació per tal d’acceptar variables a la petició HTTP (HTTP request). Per exemple, analitzant l’URL localhost:8080 /stmedioc304/medicaments/medicament?codi=M020 veieu que codi serà passat al servidor dins del cos de la petició HTTP com una variable.

Aquests tipus d’URL tenen el format urlbase/…?var1=valor1&var2=val or2&…&varn=valorn, on el símbol ”?” indica que comença la llista de variables i el símbol & separa cada variable. Cada parell variable=valor serà passat al servidor web dins del cos de l’HTTP request (petició HTTP).

Per exemple, encara que doni un error, si obriu el navegador i Firebug i provem l’URL localhost/?var1=valor1&var2=valor2&var3=valor3, veureu que heu fet un get i ha passat per paràmetre:

  • var1 valor1
  • var2 valor2
  • var3 valor3

I aquest fet no es dóna quan passeu les URL del tipus que gestionen @PathVariable i @MatrixVariable, ja que no es correspon amb la sintaxi urlbase/…?var1=valor1&var2=valor2&…&varn=valorn i, per tant, no hi ha paràmetres al cos de la petició HTTP.

A “Estoc de medicaments”, per il·lustrar el pas de variables a la petició HTTP, veureu com mostrar el detall d’un medicament a partir d’un URL del tipus esmentat, és a dir, passant els valors com a variables del cos de l’HTTP request. Per fer això fareu servir l’anotació @RequestParam.

No només veureu això, també fareu que des de qualsevol llista de medicaments, filtrada o no, es pugui anar al detall de qualsevol d’ells amb un botó, i així fareu servir el que heu preparat per mostrar el detall.

El codi del projecte “stmedioc” en l’estat detall de medicament es pot descarregar des de l’enllaç que trobareu als annexos de la unitat.

Però per seguir el desenvolupament és millor partir del que ja heu fet servir i copiar-lo amb el nom de “stmedioc304”.

Mostrant el detall

Heu de mostrar el detall de medicament en una nova vista que anomenareu medicament.jsp. Cal aquesta vista, perquè a les llistes no es mostren totes les dades del medicament, com per exemple la categoria.

Aquesta nova vista es mostrarà quan es doni una petició del tipus localhost:8080/stmedioc304/medicaments/medicament?codi=M020.

I per tant, a MedicamentController haureu d’afegir l’anotació que agafi el control i el mètode a disparar.

A les diferents capes d’Estoc de medicaments ja teniu la possibilitat d’obtenir un objecte Medicament a partir del seu codi, i per això us estalvieu codificar en altres capes, a banda de la de presentació.

A la classe MedicamentController, afegiu l’anotació i mètode amb el codi que es mostra;

    @RequestMapping("/medicament")
    public ModelAndView getMedicamentById(@RequestParam("codi") String medicamentId, HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        ModelAndView modelview = new ModelAndView("medicament");
        modelview.getModelMap().addAttribute("medicament", medicamentService.getMedicamentById(medicamentId));
        return modelview;
    }

L’anotació @RequestParam permet assignar la variable passada per paràmetre des de la petició, amb la variable que es farà servir en el mètode.

En el nostre cas, l’anotació @RequestParam enllaça el paràmetre codi del cos de la petició amb la variable medicamentId que fareu servir dins del mètode.

El mètode crida getMedicamentById de la capa de servei que ja està implementat. Aquest retorna l’objecte Medicament amb el codi que s’ha passat pel paràmetre.

A la carpeta views, afegiu la nova vista medicament.jsp amb el codi que es mostra a continuació:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
        <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
        <title>Medicament</title>
    </head>
    <body>
        <section>
            <div class="jumbotron">
                <div class="container">
                    <h1>Medicament</h1>
                </div>
            </div>
        </section>
        <section class="container">
            <div class="row">
                <div class="col-md-5">
                    <h3>${medicament.name}</h3>
                    <p>${medicament.description}</p>
                    <p>
                        <strong>Codi : </strong>${medicament.medicamentId}
                    </p>
                    <p>
                        <strong>Laboratori</strong> : ${medicament.producer}
                    </p>
                    <p>
                        <strong>Categoria</strong> : ${medicament.category}
                    </p>
                    <p>
                        <strong>Unitats en estoc </strong> :
                        ${medicament.stockQuantity}
                    </p>
                    <h4>${medicament.price} USD</h4>
                </div>
            </div>
        </section>
    </body>
</html>

La vista mostrarà els valors de l’objecte de nom medicament que havíeu afegit com a atribut al controlador.

Si executeu l’aplicació al navegador amb l’URL localhost:8080/stmedioc304/medicaments/medicament?codi=M020, el resultat és el que es veu en la figura.

Figura Sortida de detall de medicament

La part de l’URL /medicaments ha fet que el Dispatcher Servlet passi la responsabilitat a MedicamentController. L’URL és /medicaments/medicament, i com que existeix l’anotació @RequestMapping(”/medicament”) dispararà el mètode getMedicamentById que la segueix.

Aquest mètode té anotacions del tipus @RequestParam, i per tant farà servir els paràmetres del cos de la petició. En concret, l’URL conté ?codi=M020, que vol dir que caçarà el paràmetre codi a partir de @RequestParam(“codi”) String medicamentId i l’assignarà a medicamentId, que després es fa servir per cercar l’objecte Medicament amb igual codi. Aquest objecte Medicament és ficat en un atribut medicament a la resposta (response).

Llavors, la vista medicament.jsp mostra les dades d’aquest medicament fent servir l’atribut esmentat. Proveu amb altres codis de medicaments per veure’n el funcionament.

Arribant al detall des de la llista

“Estoc de medicaments” sap com mostrar el detall d’un medicament a partir de paràmetres de petició, és a dir, amb URL del tipus localhost:8080/stmedioc304/medicaments/medicament?codi=xxx, on xxx és el medicamentId.

Aprofiteu el que heu fet per mostrar el detall de qualsevol medicament a partir de la llista. Afegireu un botó a cada element de la llista que ens porti a la vista de detall. I per no anar canviant URL, fareu que des del detall es pugui tornar a la llista.

A medicaments.jsp, afegiu la directiva:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

i just després de:

<p>Hi ha ${medicament.stockQuantity} unitats en magatzem</p>

afegiu el codi següent:

                                <p>
                                    <a href=" <spring:url value= "/medicaments/medicament?codi=${medicament.medicamentId}" /> " class="btn btn-primary">
                                        <span class="glyphicon-info-sign glyphicon"/></span> Detall
                                    </a>
                                </p>

L’enllaç té l’etiqueta <spring:url>, que construirà un URL Spring MVC vàlida. En aquest cas, el codi que farà servir l’obté de l’objecte medicament de la llista que s’està recorrent.

Executeu l’aplicació amb l’URL localhost:8080/stmedioc304/medicaments/all i obtindreu un resultat com el que mostra figura.

Figura Sortida de llista de medicaments amb botó de detall

Si premeu al detall del Paracetamol, l’aplicació ha de mostrar el detall (vegeu la figura).

Figura Sortida de detall de medicament

Ara afegireu al detall un botó per tornar a la llista. Per fer això també es farà servir l’etiqueta <spring:url>.

A medicament.jsp, afegiu la directiva:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

i just després de

<h4>${medicament.price} USD</h4>

afegiu el codi següent:

                    <a href="<spring:url value="/medicaments/all" />" class="btn btn-default">
                        <span class="glyphicon-hand-left glyphicon"></span> tornar
                    </a>

Si executeu l’aplicació i aneu a mostrar la llista de medicaments ja podeu prémer un botó de detall, que farà que es mostri la vista de detall, i després tornar una altra vegada a la llista.

Què s'ha après?

MedicamentController crida directament a la capa de servei MedicamentService i no directament a la persistència.

L’anotació @RequestMapping(”/medicaments”) a nivell de classe, fa que qualsevol URL del tipus /medicaments agafi aquest controlador. Les anotacions @RequestMapping(”/xxx”) posades al davant de diversos mètodes, fa que aquests es disparin quan es fan peticions amb URL del tipus /medicaments/xxx.

Peticions com localhost:8080/stmedioc301/medicaments/Analgèsic, on “analgèsic” es pot substituir per qualsevol categoria, són ateses pel mètode que segueix a l’anotació:

@RequestMapping("/{category}")

on category és la variable de ruta (variable path) que és enllaçada a la variable medicamentCategory amb l’anotació:

@PathVariable("category") String medicamentCategory.

Les variables de matriu (matrix variables), permeten llegir URL del tipus localhost:8080/stmedioc303/medicaments/filter/ByCriteria;producer=Ferrer,Bayer;es toc=1,100, on el conjunt de parell clau=llista de valors és la matriu.

Amb l’anotació:

@RequestMapping("/filter/{ByCriteria}") 

es passa el control al mètode que la segueix, i en aquest, l’anotació:

@MatrixVariable(pathVar = "ByCriteria") Map<String, List<String>> filterParams

assigna el paràmetre (la matriu) al Map filterParams, variable que es pot fer servir dins del mètode.

Poden venir paràmetres al cos d’una petició HTTP, per exemple, a una petició Post d’un formulari.

En general, es pot atendre a peticions amb URL del tipus urlbase/…?var1=valor1&var2=valor2&…&varn=valorn, on el que segueix el símbol ? és el conjunt de paràmetres que aniran en el cos de la petició HTTP.

Per exemple, es pot resoldre una URL com:

localhost:8080/stmedioc304/medicaments/medicament?codi=M020

amb l’anotació:

@RequestMapping("/medicament") 

precedint al mètode i com a paràmetre d’aquest, l’anotació:

@RequestParam("codi") String medicamentId

que enllaça el paràmetre amb la variable medicamentId que es pot fer servir dins del mètode.

L’etiqueta de Spring <spring:url> serveix per enllaçar una pàgina amb una altra construint un URL vàlid.

Anar a la pàgina anterior:
Annexos
Anar a la pàgina següent:
Exercicis d'autoavaluació