Serveis web RESTful amb Spring. Escrivint serveis web

Explicarem, mitjançant exemples, els conceptes mes rellevants dels serveis web RESTful (de l’anglès REpresentational State Transfer) amb Spring MVC; aprendreu a crear-ne, a fer-ne el desplegament i a provar-los mitjançant exemples.

REST no és un protocol, sinó un conjunt de regles i principis que permeten desenvolupar serveis web fent servir HTTP com a protocol de comunicacions entre el client i el servei web, i es basa a definir accions sobre recursos mitjançant l’ús dels mètodes GET, POST, PUT i DELETE, inherents d’HTTP.

Per a REST, qualsevol cosa que es pugui identificar amb un URI (de l’anglès Uniform Resource Identifier) es considera un recurs i, per tant, es pot manipular mitjançant accions (també anomenades verbs) especificades a la capçalera HTTP de les peticions seguint el següent conjunt de regles i principis que regeixen REST:

  • POST: crea un recurs nou.
  • GET: consulta el recurs, n’obté la representació.
  • DELETE: esborra un recurs.
  • PUT: modifica un recurs.
  • HEAD: obté metainformació del recurs.

REST es basa en l’ús d’estàndards oberts en totes les seves parts; així, fa servir URI per a la localització de recursos, HTTP com a protocol de transport, els verbs HTTP per especificar les accions sobre els recursos i els tipus MIME per a la representació dels recursos (XML, JSON, XHTML, HTML, PDF, GIF, JPG, PNG, etc.).

Format JSON

JSON (de l’anglès Java Script Object Notation) és un format lleuger d’intercanvi de dades. És fàcil de llegir i escriure per als éssers humans i, per a les màquines, d’analitzar i generar. Això el fa ideal per representar els recursos en arquitectures REST. Un dels principals problemes dels serveis web basats en SOAP és la mida dels missatges d’intercanvi; l’ús de JSON permet minimitzar la informació a enviar.

L’especificació de Java EE 7 inclou l’API de JAX-RS (de l’anglès Java API for RESTful Web Services), que fa servir un conjunt d’anotacions per simplificar el desenvolupament, el desplegament i el consum de serveis web RESTful.

JAX-RS (de l’anglès Java API for RESTful Web Services) és l’API que inclou l’especificació de Java EE 7 per crear i consumir serveis web basats en REST.

Spring, per la seva banda, no proporciona un projecte ni un mòdul específic per a la creació i el consum de serveis web RESTful, sinó que ho engloba com una capacitat més dins el projecte Spring MVC.

Spring MVC és el mòdul del projecte Spring que proporciona suport per crear i consumir serveis web basats en REST.

JAX-RS és una API centrada únicament i exclusivament en el desenvolupament de serveis web RESTful. Spring MVC és un mòdul del projecte Spring centrat en el desenvolupament web en general, i és en aquest marc que dóna complet suport tant al desenvolupament d’aplicacions web com al desenvolupament de serveis web RESTful.

Les capacitats que ofereix Spring MVC per desenvolupar serveis web RESTful són una continuació del model més general de programació que té Spring MVC. No hi ha, doncs, un framework o mòdul específic a Spring per desenvolupar serveis web RESTful.

Per escriure un servei web RESTful, tant amb JAX-RS com amb Spring, tan sols us caldria un client i un servidor que es puguin comunicar per HTTP, però hauríeu de fer a mà tota la configuració, el parseig de les peticions segons el verb HTTP emprat, el mapatge entre el format de dades de la petició i els objectes Java que representen el domini i enviar les respostes amb el tipus MIME que s’especifiqui a la capçalera de la petició. Tant JAX-RS com Spring us estalvien tota aquesta feina proporcionant-vos un petit conjunt d’anotacions que us permetran desenvolupar còmodament serveis web RESTful.

Ens centrarem en les capacitats que proporciona Spring, i més concretament el mòdul Spring MCV, per donar suport a la creació i el consum de serveis web RESTful.

Un servei web RESTful que contesta '"Hello, World!!!"'

Veurem els diferents conceptes bàsics dels serveis web RESTful amb el típic exemple que sempre trobeu als manuals. Farem un “Hello, World!!!” i el publicarem com a servei web RESTful utilitzant Spring MVC per fer-ho.

Tot i que podeu descarregar-vos el projecte en l’estat final d’aquest apartat en l’enllaç que trobareu als annexos de la unitat, sempre és millor que aneu fent vosaltres tots els passos partint del projecte en l’estat inicial de l’apartat. Recordeu que podeu utilitzar la funció d’importar per carregar els projectes a NetBeans.

El servei web que farem tornarà “Hello, World!!!” si no li passem cap paràmetre; si li passem un paràmetre anomenat name ens tornarà una salutació personalitzada canviant la paraula World pel nom especificat a name.

Creació i configuració inicial del projecte

Com que es tracta d’un exemple molt senzill no ens cal cap projecte de partida, simplement creeu a NetBeans un nou projecte Maven de tipus Web Application. Per fer-ho, feu File / New Project i us apareixerà l’assistent de creació de projectes. A l’assistent seleccioneu Maven i Web Application, tal com es veu en la figura.

Figura Creació de projectes a NetBeans

Hem triat per a l’exemple un projecte web amb Maven, però és perfectament vàlid fer-ho amb qualsevol altre tipus de projecte web.

En la següent pantalla (vegeu la figura) triarem el nom del projecte (el podeu anomenar “Springresthelloioc”) i el paquet per defecte on anirà el codi font; per exemple, cat.xtec.ioc.springresthelloioc.

Figura Nom del nou projecte

En la següent pantalla (vegeu la figura) de l’assistent deixeu els valors per defecte i polseu Finish.

Figura Configuració del nou projecte

Un cop creat el projecte cal que modifiqueu el pom.xml per afegir les dependències cap a Spring; afegiu les següent línies al fitxer pom.xml:

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-webmvc</artifactId>
  4. <version>4.0.3.RELEASE</version>
  5. </dependency>

També heu de crear el fitxer web.xml i configurar el servlet DispatcherServlet per tal que Spring MVC serveixi les peticions web; creeu el fitxer web.xml a la carpeta Web Pages/WEB-INF amb el següent contingut:

  1. <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
  4. http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  5.  
  6. <display-name>Spring RESTful Hello World</display-name>
  7.  
  8. <servlet>
  9. <servlet-name>DispatcherServlet</servlet-name>
  10. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  11. <init-param>
  12. <param-name>contextConfigLocation</param-name>
  13. <param-value>/WEB-INF/spring/DispatcherServlet-servlet.xml</param-value>
  14. </init-param>
  15. <load-on-startup>1</load-on-startup>
  16. </servlet>
  17.  
  18. <servlet-mapping>
  19. <servlet-name>DispatcherServlet</servlet-name>
  20. <url-pattern>/</url-pattern>
  21. </servlet-mapping>
  22. </web-app>

Ens cal també crear el fitxer de configuració per a Spring; li hem indicat que el fitxer s’anomenarà DispatcherServlet-servlet.xml i serà a la ruta /WEB-INF/spring/. Creeu primer la carpeta /WEB-INF/spring/ i després el fitxer DispatcherServlet-servlet.xml amb el següent contingut:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:mvc="http://www.springframework.org/schema/mvc"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans.xsd
  8. http://www.springframework.org/schema/context
  9. http://www.springframework.org/schema/context/spring-context-4.0.xsd
  10. http://www.springframework.org/schema/mvc
  11. http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
  12. <mvc:annotation-driven />
  13. <context:component-scan base-package="cat.xtec.ioc" />
  14.  
  15. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  16. <property name="prefix" value="/WEB-INF/views/" />
  17. <property name="suffix" value=".jsp" />
  18. </bean>
  19.  
  20. </beans>

Si recarregueu el pom.xml fent clic amb el botó dret damunt el nom del projecte i prement l’opció Reload POM del menú contextual ja tindreu el projecte configurat i llest per començar a crear el servei web RESTful amb Spring.

Creació del servei web RESTful

Ja heu creat el projecte que us servirà de base, ara cal que codifiqueu el servei web RESTful que ha de tornar la salutació “Hello, World!!!”; per fer-ho cal decidir primer dues coses: amb quin dels verbs HTTP ha de respondre el servei web i quin URI fareu servir per cridar-lo.

Aquest servei web ha d’obtenir una representació del recurs en format JSON, i per tant haurà de respondre el verb GET.

La decisió sobre quin URI utilitzar és força arbitrària; en aquest cas utilitzarem, per exemple, /hello.

Per tant, a les peticions d’aquest tipus:

GET http://localhost:8080/resthelloioc/hello

ha de respondre amb:

{"id":1,"content":" Hello, World!!!"}

I a les peticions d’aquest tipus:

GET http://localhost:8080/resthelloioc/hello?name=User

ha de respondre amb:

{"id":1,"content":"Hello, User!"}

JSON

A l’exemple us demanem que treballeu amb una representació JSON. La simplicitat, lleugeresa i facilitat de lectura fan ideals aquesta representació per treballar amb aplicacions i dispositius que tenen restriccions pel que fa al volum de dades a intercanviar. Les aplicacions mòbils que consumeixin dades de serveis web RESTful són un molt bon exemple en aquest sentit; la quantitat d’informació que s’intercanviaran client i servidor per fer les operacions és molt menor en una aproximació JSON + RESTful que en una aproximació XML + SOAP.

El servei web tornarà la informació en format JSON. A la sortida, el camp id és un identificador únic per a la salutació, i el camp content representa la salutació.

Un cop decidit el funcionament del nostre servei, el següent serà implementar la classe que modelarà la representació de la salutació, és a dir, la classe que modelarà el que torna el servei web.

Creeu una classe Java anomenada Greeting al paquet cat.xtec.ioc. springresthelloioc.domain amb el següent codi:

  1. public class Greeting {
  2.  
  3. private final long id;
  4. private final String content;
  5.  
  6. public Greeting(long id, String content) {
  7. this.id = id;
  8. this.content = content;
  9. }
  10.  
  11. public long getId() {
  12. return id;
  13. }
  14.  
  15. public String getContent() {
  16. return content;
  17. }
  18. }

Spring fa servir la llibreria Jackson per fer les transformacions entre el format JSON i la seva representació Java, i a l’inrevés.

El següent pas és crear el controlador Spring, que s’encarregarà de servir les peticions; per fer-ho, creeu una classe Java anomenada GreetingController al paquet cat.xtec.ioc.springresthelloioc.controller amb el següent codi:

  1. package cat.xtec.ioc.springresthelloioc.controller;
  2.  
  3. import cat.xtec.ioc.springresthelloioc.domain.Greeting;
  4. import java.util.concurrent.atomic.AtomicLong;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.RequestMethod;
  7. import org.springframework.web.bind.annotation.RequestParam;
  8. import org.springframework.web.bind.annotation.RestController;
  9.  
  10. @RestController
  11. public class GreetingController {
  12.  
  13. private static final String template = "Hello, %s";
  14. private final AtomicLong counter = new AtomicLong();
  15.  
  16. @RequestMapping(method = RequestMethod.GET, value = "/hello")
  17. public Greeting greeting(@RequestParam(value="name", defaultValue="World!!!") String name) {
  18. return new Greeting(counter.incrementAndGet(),
  19. String.format(template, name));
  20. }
  21. }

El codi del controlador és molt simple, però porta algunes particularitats que cal remarcar; fixeu-vos que:

  • Hem anotat la classe amb @RestController per marcar la classe com un controlador REST on cada un dels seus mètodes tornarà un objecte del domini enlloc d’una vista. Aquesta és la diferència més gran entre un controlador MVC típic i un controlador per serveis web RESTful: el cos de la resposta HTTP es crea transformant un objecte del domini a JSON, i no es torna en format HTML.
  • L’objecte Greeting que torna el mètode greeting cal que sigui transformat a JSON; Spring s’encarrega de fer això automàticament.
  • Hem anotat el mètode greeting amb @RequestMapping indicant que les peticions a /hello les servirem amb aquest mètode.
  • Hem anotat el mètode greeting amb RequestMethod.GET per indicar que respondrà a les peticions HTTP que es facin mitjançant el verb GET. Si no ho haguéssim fet, el mètode respondria a qualsevol dels verbs HTTP.
  • Hem anotat el paràmetre del mètode greeting amb @RequestParam per indicar que el paràmetre name de la petició HTTP s’ha de vincular amb el paràmetre name del mètode. El paràmetre és opcional i, si no ve a la petició HTTP, farem servir “World!!!” com a valor per defecte.
  • En la implementació simplement creem i retornem l’objecte Greeting, que modela la salutació.
  • Com que hi ha la llibreria Jackson al classpath, Spring tria la conversió de l’objecte Greeting a JSON per defecte.

I aquesta és tota la feina que cal fer per implementar el servei web, Spring farà la resta. Ara tan sols manca fer-ne el desplegament al servidor d’aplicacions i provar-lo.

Desplegament i prova del servei web RESTful

Un cop creat el servei web, cal que el desplegueu per tal de fer-lo accessible als clients i poder-lo provar. El procés de desplegament del servei web es fa desplegant l’aplicació Java EE que el conté mitjançant els mecanismes normals de desplegament de qualsevol aplicació Java EE.

Aquest procés és molt senzill, simplement cal que feu Clean and Build i després Run a NetBeans, i es farà el desplegament al servidor d’aplicacions que tingueu configurat per al projecte.

Si proveu ara accedint a l’URL localhost:8080/springresthelloioc/hello amb qualsevol navegador veureu que el servei web us torna la salutació en format JSON:

{"id":1,"content":"Hello, World!!!!"}

Proveu també especificant el paràmetre name, accedint a localhost:8080/springresthelloioc/hello?name=User, per exemple, i veureu que la salutació canvia:

{"id":2,"content":"Hello, User!"}

Amb això ja heu creat, configurat, desplegat i provat un servei web RESTful molt senzill amb Spring.

Testejant el servei web '"Hello, World!!!"'

Aprofitarem les capacitats que proporcions Spring, i més concretament Spring MVC, per crear un conjunt de tests que van a cavall entre un test unitari i un test d’integració. Aquests tests us permetran provar els serveis web sense necessitat de tenir-los desplegats i executant-se a un servidor d’aplicacions. Testejarem amb Spring el típic exemple de “Hello, World!!!”.

Creació i configuració inicial del projecte

El projecte té un servei web RESTful configurat a la classe GreetingController amb el següent codi:

Descarregueu el codi del projecte “Springtestresthelloioc” en l’estat inicial d’aquest apartat en l’enllaç que trobareu als annexos de la unitat i importeu-lo a NetBeans.

  1. package cat.xtec.ioc.springresthelloioc.controller;
  2.  
  3. import cat.xtec.ioc.springresthelloioc.domain.Greeting;
  4. import java.util.concurrent.atomic.AtomicLong;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.RequestMethod;
  7. import org.springframework.web.bind.annotation.RequestParam;
  8. import org.springframework.web.bind.annotation.RestController;
  9.  
  10. @RestController
  11. public class GreetingController {
  12.  
  13. private static final String template = "Hello, %s";
  14. private final AtomicLong counter = new AtomicLong();
  15.  
  16. @RequestMapping(method = RequestMethod.GET, value = "/hello")
  17. public Greeting greeting(@RequestParam(value="name", defaultValue="World!!!") String name) {
  18. return new Greeting(counter.incrementAndGet(),
  19. String.format(template, name));
  20. }
  21. }

Aquest servei web és un servei web RESTful desenvolupat amb Spring que torna “Hello, World!!!” si no li passem cap paràmetre, i si li passem un paràmetre anomenat name torna una salutació personalitzada canviant la paraula World pel nom especificat a name.

Tot i que podeu descarregar-vos el projecte en l’estat final d’aquest apartat en l’enllaç que trobareu als annexos de la unitat, sempre és millor que aneu fent vosaltres tots els passos partint del projecte en l’estat inicial de l’apartat. Recordeu que podeu utilitzar la funció d’importar per carregar els projectes a NetBeans.

El servei web torna la salutació en format JSON i respon a les peticions GET a /hello amb:

{"id":1,"content":" Hello, World!!!"}

I a les peticions GET a /hello?name=User amb:

{"id":1,"content":"Hello, User!"}

El test que escriurem utilitzarà les capacitats que té Spring MVC per fer test unitari dels endpoints configurats a un controlador RESTful. La principal diferència entre aquest tipus de tests i els tests d’integració és que en aquest cas no ens cal tenir el servei web que volem provar desplegat i executant-se a un servidor d’aplicacions.

Els tests d’integració difereixen dels tests unitaris, ja que no testegen el codi de forma aïllada, sinó que requereixen que el codi a testejar estigui desplegat al servidor d’aplicacions per funcionar.

Els tests unitaris amb el bastiment proporcionat per Spring MVC es basen en JUnit 4. JUnit és un framework de test que utilitza anotacions per identificar els mètodes que especifiquen un test. A JUnit, un test, ja sigui unitari o d’integració, és un mètode que s’especifica en una classe que només s’utilitza per al test. Això s’anomena una classe de test. Un mètode de test amb JUnit 4 es defineix amb l’anotació @org.junit.Test. En aquest mètode s’utilitza un mètode d’asserció en el qual es comprova el resultat esperat de l’execució de codi en comparació del resultat real.

Creació i execució dels tests unitaris

Ara toca començar a crear els tests d’integració per provar el servei web. Per fer-ho, creeu un nou paquet dins de Test Packages anomenat, per exemple, cat.xtec.ioc.springresthelloioc.controller, i una classe Java anomenada GreetingControllerTest:

  1. package cat.xtec.ioc.springresthelloioc.controller;
  2.  
  3. import org.junit.Before;
  4. import org.junit.Test;
  5. import org.junit.runner.RunWith;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.http.MediaType;
  8. import org.springframework.http.converter.HttpMessageConverter;
  9. import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
  10. import org.springframework.mock.http.MockHttpOutputMessage;
  11. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  12. import org.springframework.test.context.web.WebAppConfiguration;
  13. import org.springframework.test.web.servlet.MockMvc;
  14. import org.springframework.web.context.WebApplicationContext;
  15.  
  16. import java.io.IOException;
  17. import java.nio.charset.Charset;
  18. import java.util.Arrays;
  19. import static junit.framework.Assert.assertNotNull;
  20. import static org.hamcrest.CoreMatchers.is;
  21.  
  22. import org.springframework.boot.test.SpringApplicationConfiguration;
  23. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
  24. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
  25. import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
  26.  
  27.  
  28. @RunWith(SpringJUnit4ClassRunner.class)
  29. @SpringApplicationConfiguration(classes = Application.class)
  30. @WebAppConfiguration
  31. public class GreetingControllerTest {
  32. private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
  33. MediaType.APPLICATION_JSON.getSubtype(),
  34. Charset.forName("utf8"));
  35.  
  36. private MockMvc mockMvc;
  37.  
  38. private HttpMessageConverter mappingJackson2HttpMessageConverter;
  39.  
  40. @Autowired
  41. private WebApplicationContext webApplicationContext;
  42.  
  43. @Autowired
  44. void setConverters(HttpMessageConverter<?>[] converters) {
  45. this.mappingJackson2HttpMessageConverter = Arrays.asList(converters).stream()
  46. .filter(hmc -> hmc instanceof MappingJackson2HttpMessageConverter)
  47. .findAny()
  48. .orElse(null);
  49.  
  50. assertNotNull("the JSON message converter must not be null",
  51. this.mappingJackson2HttpMessageConverter);
  52. }
  53.  
  54. @Before
  55. public void setup() throws Exception {
  56. this.mockMvc = webAppContextSetup(webApplicationContext).build();
  57.  
  58. }
  59.  
  60.  
  61. protected String json(Object o) throws IOException {
  62. MockHttpOutputMessage mockHttpOutputMessage = new MockHttpOutputMessage();
  63. this.mappingJackson2HttpMessageConverter.write(
  64. o, MediaType.APPLICATION_JSON, mockHttpOutputMessage);
  65. return mockHttpOutputMessage.getBodyAsString();
  66. }
  67. }

Fixeu-vos que:

  • Anotem la classe amb @RunWith(SpringJUnit4ClassRunner.class) per indicar que es tracta d’un test unitari que s’executarà amb l’executor de JUnit que porta Spring.
  • Anotem la classe amb @SpringApplicationConfiguration(classes = Application.class) per indicar que crearem una classe que carregui la configuració Spring. Aquesta és una de les diverses maneres de fer-ho, també es podria carregar la configuració amb fitxers XML.
  • Anotem la classe amb @WebAppConfiguration per indicar a JUnit que es tracta d’un test unitari per a Spring MVC i que s’ha d’executar amb un context d’aplicació web i no d’aplicació stand-alone.
  • Anotem un mètode anomenat setup amb @Before per indicar a JUnit que aquest mètode s’executi cada cop que es creï la classe de test. En aquest mètode creem un objecte de tipus MockMvc que serà el que ens proporcionarà tota la infraestructura necessària per fer els tests sense haver de desplegar l’aplicació a cap servidor d’aplicacions.
  • El mètode json és un mètode que ens torna la representació en format JSON d’un objecte.
  • S’han configurat tots els conversors que es requereixen per parsejar les peticions i les respostes.

Per tal que Spring pugui carregar la configuració cal una classe anomenada Application al paquet cat.xtec.ioc.springresthelloioc.controller amb el següent codi:

  1. package cat.xtec.ioc.springresthelloioc.controller;
  2.  
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5.  
  6. @SpringBootApplication
  7. public class Application {
  8.  
  9. public static void main(String[] args) {
  10. SpringApplication.run(Application.class, args);
  11. }
  12. }

Aquesta classe ja la teniu creada al projecte de partida.

El primer test que farem serà un test que comprovi que la petició /hello sense cap paràmetre torna la representació JSON de la salutació “Hello, World!!!; per fer-ho, creeu un mètode anomenat greetingShouldReturnHelloWorldWithoutName dins la classe GreetingControllerTest amb el següent codi:

  1. @Test
  2. public void greetingShouldReturnHelloWorldWithoutName() throws Exception {
  3. mockMvc.perform(get("/hello"))
  4. .andExpect(status().isOk())
  5. .andExpect(content().contentType(contentType))
  6. .andExpect(jsonPath("$.content", is("Hello, World!!!")));
  7. }

Si analitzem el codi veiem diverses coses importants: la primera és que hem anotat el mètode greetingShouldReturnHelloWorldWithoutName amb l’anotació @Test per indicar que es tracta d’un mètode de test.

També que fa servir l’objecte MockMVC que heu creat per fer una petició de tipus GET a l’ URI /hello; comproveu que el resultat de la petició sigui un 200 (OK), que el content type sigui JSON i que tingui al camp content la salutació per defecte ”Hello, World!!!”.

Ja podem executar el test que hem creat fent Test File (vegeu la figura) al menú contextual de la classe de test.

Figura Execució del test

Si tot ha anat bé veureu el resultat a la finestra de Test (vegeu la figura).

Figura Resultat de l’execució del test

Tot i que el resultat de l’execució dels test es pot veure a la finestra de sortida de NetBeans, és molt més còmode visualitzar-ho a la finestra de resultats de tests que proporciona també NetBeans; per mostrar aquesta finestra aneu al menú Window / IDE Tools / Test Results (vegeu la figura).

Figura Finestra de resultats dels tests

A Netbeans, els tests es poden executar de forma individual, és a dir, classe per classe o tots els del projecte. Si voleu executar tots els tests d’un projecte, primer heu de designar com a projecte principal el projecte “Springtestresthelloioc”, tal com podeu veure en la figura.

Figura Assignació del projecte com a projecte principal

El segon test que farem serà un test que comprovi que la petició /hello?name=User torna la representació JSON de la salutació “Hello, User; per fer-ho, creeu un mètode anomenat greetingShouldReturnHelloNameWithName dins la classe GreetingControllerTest amb el següent codi:

  1. @Test
  2. public void greetingShouldReturnHelloNameWithName() throws Exception {
  3. mockMvc.perform(get("/hello?name=User"))
  4. .andExpect(status().isOk())
  5. .andExpect(content().contentType(contentType))
  6. .andExpect(jsonPath("$.content", is("Hello, User")));
  7. }

El codi és molt similar al primer test; executeu-lo de la mateixa manera i comproveu que el test s’executa correctament (vegeu la figura).

Figura Resultat de l’execució dels dos tests

Aquest dos exemples us donen la base per tal que pugueu fer tots els tests unitaris que se us acudeixin per als serveis web RESTful que desenvolupeu amb Spring i així tenir-los totalment provats.

El servei web de gestió d'equips de futbol. Operacions CRUD

Veurem els conceptes referents a la creació de serveis web RETSful amb Spring desenvolupant un servei web RESTful que permeti fer operacions sobre equips de futbol i els jugadors que els integren. Farem les operacions típiques CRUD amb els jugadors, una operació que mostra el llistat d’equips, una que mostra els jugadors d’un equip i una altra que mostra les dades d’un equip.

CRUD és l’acrònim en anglès de les operacions de creació (Create), lectura (Read), actualització (Update) i esborrat (Delete).

Concretament, el servei web que farem tindrà les següents operacions:

  • llistar tots els equips
  • consulta de les dades d’un equip
  • llistar tots els jugadors d’un equip
  • consulta d’un jugador d’un equip (operació Read CRUD)
  • creació d’un jugador en un equip (operació Create CRUD)
  • actualització de les dades d’un jugador d’un equip (operació Update CRUD)
  • esborrar un jugador d’un equip (operació Delete CRUD)

Per fer-ho emprareu una aplicació web ja desenvolupada que segueix una arquitectura per capes i hi afegireu una capa de serveis on creareu i publicareu el servei web de gestió d’equips catàleg com a servei web RESTful. La representació dels recursos que fareu servir en tot l’exemple serà JSON.

Creació i configuració inicial del projecte

L’aplicació de la qual partireu s’anomena “Springrestteamsioc” i servirà per veure com podeu exposar algunes funcionalitats de la capa de serveis d’una aplicació mitjançant serveis web RESTful. Es tracta d’un projecte Spring MVC senzill que segueix una arquitectura típica per capes per tal d’aconseguir una alta reusabilitat, un baix acoblament i una alta cohesió a l’aplicació.

El projecte consta de quatre capes:

  • capa de presentació
  • capa de domini
  • capa de serveis
  • capa de persistència

Com que l’aplicació d’exemple no ha de proporcionar interfície gràfica, fareu servir la capa de presentació com la capa on publicareu els serveis RESTful; seria igualment vàlid fer-ho a una capa de serveis independent.

El model de domini ja el teniu implementat i és molt senzill: hi ha una entitat Team que representa un equip i una entitat Player que representa els jugadors d’un equip. Entre Team i Player hi ha una associació 1:N per indicar que un jugador pertany a un equip i que un equip està format per N jugadors (vegeu la figura).

Figura Entitats Team i Player

La representació en JSON d’un equip és la següent:

{ 
  "id":1,
  "name":"F.C. Barcelona",
  "foundationYear":"1899"
}

Repositoris 'in memory'

Tot i que podríem haver optat per fer l’exemple amb un repositori connectat a una font de dades persistent com una base de dades relacional i utilitzar l’API JPA per definir les entitats del projecte, s’ha considerat que això afegeix “soroll” a l’exemple i us faria fer algunes tasques de configuració que no són pròpies de l’objectiu principal. Per aquest motiu fareu servir repositoris in memory tant per als equips com per als jugadors.

I la representació JSON d’un jugador és la següent:

{
  "id":1,
  "name":"Lionel Messi",
  "goals":472,
  "age":29,
  "teamId":1
}

La capa de serveis serà la que treballarem i haurà de proporcionar un servei web RESTful que permeti als clients fer les següents operacions sobre els equips:

  • llistar tots els equips
  • consulta de les dades d’un equip
  • llistar tots els jugadors d’un equip
  • consulta d’un jugador d’un equip
  • creació d’un jugador en un equip
  • actualització de les dades d’un jugador d’un equip
  • esborrar un jugador d’un equip

La capa de persistència també la teniu implementada i conté dos objectes repositori que permeten mapar les dades de les fonts de dades amb els objectes del domini. En el nostre cas, per simplicitat, farem servir repositoris in memory que tindran una llista precarregada amb els equips i els jugadors de cada equip.

Creació i prova del servei web RESTful

Un cop fet, creareu una classe que exposarà les operacions que voleu realitzar sobre els equips; anomenareu la classe TeamsController, la creareu al paquet cat.xtec.ioc.controller i l’anotareu amb @RestController:

Descarregueu el codi del projecte “Springrestteamsioc” de l’enllaç que trobareu als annexos de la unitat i importeu-lo a NetBeans.

  1. package cat.xtec.ioc.controller;
  2.  
  3. import org.springframework.web.bind.annotation.RestController;
  4.  
  5. @RestController
  6. public class TeamsController {
  7. }

Tot i que podeu descarregar-vos el projecte en l’estat final d’aquest apartat en enllaç que trobareu als annexos de la unitat, sempre és millor que aneu fent vosaltres tots els passos partint del projecte en l’estat inicial de l’apartat. Recordeu que podeu utilitzar la funció d’importar per carregar els projectes a NetBeans.

Simplement heu anotat la classe amb @RestController per marcar la classe com un controlador REST on cada un dels seus mètodes tornarà un objecte del domini enlloc d’una vista. Aquesta és la diferència més gran entre un controlador MVC típic i un controlador per a serveis web RESTful: el cos de la resposta HTTP es crea transformant un objecte del domini a JSON i no es torna en format HTML.

Un cop creada la classe cal que hi afegiu una referència al repositori dels equips fent que Spring el carregui amb el seu mòdul d’injecció de dependències:

  1. @Autowired
  2. private TeamRepository teamRepository;
  3.  
  4. public TeamsController() {
  5. }
  6.  
  7. public TeamsController(TeamRepository teamRepository) {
  8. this.teamRepository = teamRepository;
  9. }

Quan NetBeans us demani quins imports voleu afegir especifiqueu, a la major part de casos, els del paquet org.springframework.web. bind.annotation.

I ja podeu començar a codificar el primer servei que voleu oferir; per exemple, la consulta de tots els equips. Per fer-ho, creeu un mètode anomenat getAll amb el següent codi:

  1. @RequestMapping(method = RequestMethod.GET, value = "/teams")
  2. public @ResponseBody List<Team> getAll() {
  3. return this.teamRepository.getAll();
  4. }

Hem anotat el mètode amb l’anotació @RequestMapping(method = RequestMethod.GET, value = ”/teams”) per indicar que aquest mètode respondrà a peticions HTTP de tipus GET a l’URL /teams.

Hem anotat el mètode amb @ResponseBody per indicar a Spring que torni el resultat en un format que sigui entenedor per al client a partir de les capçaleres HTTP que envia amb la petició. Per defecte, es torna en format JSON.

Tornem la llista d’equips com una llista d’objectes Java de tipus Team i Spring s’encarregarà de transformar-los a format JSON.

Per provar si funciona, desplegueu el projecte fent Run a NetBeans i accediu amb un navegador a l’URL localhost:8080/springrestteamsioc/teams; si tot ha anat bé, veureu la representació JSON dels equips al navegador:

[
{"id":1,"name":"F.C.Barcelona","foundationYear":"1899"},
{"id":2,"name":"Real Madrid","foundationYear":"1902"}
]

Passem a crear ara el servei de consulta de les dades d’un equip; per fer-ho, creeu un mètode anomenat getById amb el següent codi:

  1. @RequestMapping(value = "/teams/{id}", method = RequestMethod.GET)
  2. public @ResponseBody Team getById(@PathVariable int id) {
  3. return this.teamRepository.get(id);
  4. }

El codi és molt similar a la consulta d’equips, però com a particularitat cal comentar que ara tornem un objecte Java de tipus Team que Spring convertirà a JSON abans de tornar-lo al client i que hem anotat el mètode amb l’anotació @RequestMapping(value = ”/teams/{id}”, method = RequestMethod.GET) per indicar que la informació de l’equip la tornarem quan ens arribin peticions GET a l’URI /teams/{id}; és a dir, el que posem darrere de /teams serà l’identificador de l’equip que volem consultar.

El paràmetre que rep el mètode s’ha anotat amb l’anotació @PathVariable int id. Aquesta és la forma que proporciona Spring per extreure els paràmetres d’una petició. En aquest cas estem extraient un paràmetre del path i el passem com a int al mètode getById.

Spring MVC proporciona un ampli conjunt d’anotacions per fer aquesta tasca fàcil, entre elles @PathParam, @RequestParam, @CookieValue, @RequestHeader, etc.

Per exemple, podríem utilitzar @RequestParam(“id”) per extreure un paràmetre que vingués en una petició d’aquesta forma: localhost:8080/springrestteamsioc/teams?id=2.

Per provar si funciona desplegueu el projecte fent Run a NetBeans i accediu amb un navegador a l’URL localhost:8080/springrestteamsioc/teams/1; si tot ha anat bé, veureu la representació JSON de l’equip consultat al navegador:

[{ 
  "id":1,
  "name":"F.C. Barcelona",
  "foundationYear":"1899"
}]

Ja heu creat les dues operacions que volíeu per als equips, ara començareu amb les operacions que voleu oferir per als jugadors d’un equip. Aquestes operacions les podríeu posar el mateix controlador on heu posat les operacions per als equips, però per fer un codi més net i que respecti al màxim el principi de disseny SRP (de l’anglès Single Responsability Principle) ho fareu en un controlador diferent.

Creeu una classe que exposarà les operacions que voleu realitzar sobre els jugadors dels equips; anomenareu la classe PlayerController, la creareu al paquet cat.xtec.ioc.controller i l’anotareu amb @RestController:

  1. package cat.xtec.ioc.controller;
  2.  
  3. import org.springframework.web.bind.annotation.RestController;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5.  
  6. @RestController
  7. @RequestMapping("/teams/{teamId}/players")
  8. public class PlayerController {
  9. }

Simplement heu anotat la classe amb @RestController per marcar la classe com un controlador REST on cada un dels seus mètodes tornarà un objecte del domini enlloc d’una vista.

També heu anotat la classe amb @RequestMapping(”/teams/{teamId}/players”) per indicar que tots els mètodes del controlador s’han d’accedir amb aquest format. Amb això, tots els mètodes del controlador podran extreure del path l’identificador de l’equip i no us caldrà repetir-ho a cada un d’ells.

Un cop creada la classe, cal que hi afegiu una referència al repositori dels jugadors dels equips fent que Spring el carregui amb el seu mòdul d’injecció de dependències:

  1. @Autowired
  2. private PlayerRepository playerRepository;
  3.  
  4. public PlayerController() {
  5. }
  6.  
  7. public PlayerController(PlayerRepository playerRepository) {
  8. this.playerRepository = playerRepository;
  9. }

Creeu primer la funcionalitat que permeti la consulta de tots els jugadors d’un equip. Per fer-ho, creeu un mètode anomenat getTeamPlayers amb el següent codi:

  1. @RequestMapping(method = RequestMethod.GET)
  2. public @ResponseBody List<Player> getTeamPlayers(@PathVariable int teamId) {
  3. return this.playerRepository.getByTeam(teamId);
  4. }

Heu anotat el mètode amb l’anotació @RequestMapping(method = RequestMethod.GET) per indicar que aquest mètode respondrà a peticions HTTP de tipus GET i ho farà a l’URI per defecte que heu configurat al controlador (/teams/{teamId}/players).

Heu anotat el mètode amb @ResponseBody per indicar a Spring que torni el resultat en un format que sigui comprensible per al client a partir de les capçaleres HTTP que el client envia amb la petició. Per defecte, es torna en format JSON.

Torneu la llista d’equips com una llista d’objectes Java de tipus Player i Spring s’encarregarà de transformar-los a format JSON.

Per provar si funciona, desplegueu el projecte fent Run a NetBeans i accediu amb un navegador a l’URL localhost:8080/springrestteamsioc/teams/1/players; si tot ha anat bé, veureu la representació JSON dels jugadors del FC Barcelona al navegador:

[
{"id":1,"name":"LionelMessi","goals":472,"age":29,"teamId":1},
{"id":2,"name":"Luis Suarez","goals":100,"age":29,"teamId":1}}
]

Passareu a crear ara el servei de consulta de les dades d’un jugador d’un equip (l’operació Read de CRUD); per fer-ho, creeu un mètode anomenat getById amb el següent codi:

  1. @RequestMapping(value = "{playerId}", method = RequestMethod.GET)
  2. public @ResponseBody Player getById(@PathVariable int teamId, @PathVariable int playerId) {
  3. return this.playerRepository.get(teamId, playerId);
  4. }

El codi és molt similar a la consulta dels jugadors d’un equip; com a particularitat, cal comentar que ara tornem un objecte Java de tipus Player que Spring convertirà a JSON abans de tornar-lo al client i que hem anotat el mètode amb l’anotació @RequestMapping(value = ”{playerId}”, method = RequestMethod.GET) per indicar que la informació del jugador la tornarem quan ens arribin peticions GET a l’URI /teams/{teamId}/players/{playerId}; és a dir, el que posem darrere de /teams serà l’identificador de l’equip al qual pertany el jugador, i el que posem darrere de players serà l’identificador del jugador que volem consultar.

Els paràmetres que rep el mètode s’han anotat amb les anotacions @PathVariable int teamId i @PathVariable int playerId, respectivament. Aquesta és la forma que proporciona Spring per extreure els paràmetres d’una petició. En aquest cas, estem extraient dos paràmetres del path i els passem com a int al mètode getById.

Per provar si funciona, desplegueu el projecte fent Run a NetBeans i accediu amb un navegador a l’URL localhost:8080/springrestteamsioc/teams/1/players/1; si tot ha anat bé, veureu la representació JSON del jugador “Lionel Messi” del “FC Barcelona” al navegador:

[{ 
  {"id":1,
   "name":"Lionel Messi",
   "goals":472,
   "age":29,
   "teamId":1}
 }
]

Un cop fetes les dues operacions de consulta sobre els jugadors d’un equip passareu ara a crear el servei que permeti donar d’alta un jugador d’un equip (l’operació Create de CRUD); per fer-ho, creeu un mètode anomenat create amb el següent codi:

  1. @RequestMapping(method = RequestMethod.POST)
  2. public @ResponseBody ResponseEntity<Player> create(@PathVariable int teamId, @RequestBody Player player) {
  3. player.setTeamId(teamId);
  4. this.playerRepository.add(player);
  5. return new ResponseEntity<>(player, HttpStatus.CREATED);
  6. }

Heu anotat el mètode amb l’anotació @RequestMapping(method = RequestMethod.POST) per indicar que aquest mètode respondrà a peticions HTTP de tipus POST i ho farà a l’URI per defecte que heu configurat al controlador (/teams/{teamId}/players).

La resposta que tornem és de tipus ResponseEntity<Player> per tal que hi puguem afegir la representació del jugador creat i el codi HTTP (en aquest cas un codi 201, CREATED, per indicar que el recurs s’ha creat satisfactòriament).

Fixeu-vos que tan sols amb aquesta informació Spring és capaç, quan rep una petició POST a l’URI /teams/{teamId}/players amb una representació JSON d’un jugador, de crear un objecte de tipus Player i passar-lo al mètode create.

Per documentar i provar API RESTful teniu moltes alternatives, entre les quals cal destacar Swagger swagger.io i Postman www.getpostman.com, que és una extensió per al navegador Google Chrome.

Ara tocaria provar aquesta funcionalitat, però tenim un problema: com podem enviar peticions POST sense fer una aplicació web amb un formulari? Per fer una petició GET tan sols hem de posar l’URL al navegador i ja ho tenim, però per fer peticions POST caldrà alguna utilitat extra. La nostra proposta és que feu servir cURL, que és una eina molt útil per fer peticions HTTP de diferents tipus i és multiplataforma. Si feu servir Linux possiblement ja la tingueu instal·lada al sistema; en cas que utilitzeu Windows, la podeu descarregar en el següent enllaç: curl.haxx.se/download.html.

Afegiu el paràmetre -v (verbose) a les crides a cURL si voleu veure més informació sobre les peticions i respostes que feu.

La sintaxi de cURL per fer peticions és molt senzilla; per exemple, per consultar tots els equips feu un command prompt:

curl localhost:8080/springrestteamsioc/teams

Per provar la funcionalitat que permet afegir un jugador al FC Barcelona desplegueu el projecte fent Run a NetBeans i feu una petició POST a l’URL localhost:8080/springrestteamsioc /teams/1/players especificant la informació del jugador amb JSON; això ho podeu fer amb cURL i la següent comanda:

curl -H "Content-Type: application/json" -X POST -d "{\"id\":\"3\",\"name\":\"Neymar da Silva Santos\",\"age\":\"24\",\"goals\":\"100\"}" http://localhost:8080/springrestteamsioc/teams/1/players

Fixeu-vos: li diem que farem una petició POST amb el paràmetre -X, li especifiquem el format JSON del jugador amb el paràmetre -d i li indiquem que el format és JSON especificant-ho a la capçalera amb el paràmetre -H.

Executeu la comanda anterior i després consulteu els jugadors del FC Barcelona amb:

curl localhost:8080/springrestteamsioc/teams/1/players

Si tot ha anat bé, veureu que Neymar s’ha afegit com a jugador del FC Barcelona:

[
 {"id":1,"name":"Lionel Messi","goals":472,"age":29,"teamId":1},
 {"id":2,"name":"Luis Suarez","goals":100,"age":29,"teamId":1},
 {"id":3,"name":"Neymar da Silva Santos","goals":100,"age":24,"teamId":1}
]

Creem ara el servei que permeti modificar la informació d’un jugador (l’operació Update de CRUD); per fer-ho, creeu un mètode anomenat update amb el següent codi:

  1. @RequestMapping(method = RequestMethod.PUT)
  2. public @ResponseBody ResponseEntity<Player> update(@PathVariable int teamId, @RequestBody Player player) {
  3. player.setTeamId(teamId);
  4. this.playerRepository.update(player);
  5. return new ResponseEntity<>(player, HttpStatus.OK);
  6. }

Les consideracions en aquest cas són les mateixes que hem vist per al cas de la creació d’un jugador, l’únic que canvia és que farem servir el verb PUT en lloc del verb POST.

Per provar la funcionalitat que permet modificar la informació d’un jugador desplegueu el projecte fent Run a NetBeans i feu una petició PUT a l’URL localhost:8080/springrestteamsioc /teams/1/players especificant la nova informació del jugador; això ho podeu fer amb cURL i la següent comanda:

curl -H "Content-Type: application/json" -X PUT -d "{\"id\":\"3\",\"name\":\"Neymar da Silva Santos\",\"age\":\"24\",\"goals\":\"120\"}" http://localhost:8080/springrestteamsioc/teams/1/players

Pèrdua de dades

Us pot passar que en afegir el mètode update per modificar la informació d’un jugador es torni a desplegar l’aplicació, i, com que feu servir un repositori in memory, es perdin les dades dels jugadors que heu donat d’alta. Si us trobeu en aquest cas simplement cal que torneu a fer l’alta del jugador Neymar i després en feu la modificació.

Executeu la comanda anterior i després consulteu la informació del jugador que heu modificat amb:

curl http://localhost:8080/springrestteamsioc/teams/1/players/3

Si tot ha anat bé, veureu que el nombre de gols que ha marcat Neymar ha passat de 100 a 120:

[ 
 {"id":3,"name":"Neymar da Silva Santos","goals":120,"age":24,"teamId":1}
]

Creem ara el servei que permeti esborrar un jugador d’un equip (l’operació Delete de CRUD); per fer-ho, creeu un mètode anomenat delete amb el següent codi:

  1. @RequestMapping(value = "{playerId}", method = RequestMethod.DELETE)
  2. public @ResponseBody ResponseEntity<Player> delete(@PathVariable int teamId, @PathVariable int playerId) {
  3. this.playerRepository.delete(teamId, playerId);
  4. return new ResponseEntity<>(HttpStatus.OK);
  5. }

Les consideracions en aquest cas són les mateixes que hem vist per al cas de la consulta d’un jugador d’un equip, i l’únic que canvia és que fareu servir el verb DELETE en lloc del verb GET.

Per provar la funcionalitat que permet esborrar un jugador d’un equip desplegueu el projecte fent Run a NetBeans i feu una petició DELETE a l’URL localhost:8080/springrestteamsioc/{teamId}/players/{playerId} especificant l’equip al qual pertany el jugador i l’identificador del jugador que volem esborrar; això ho podeu fer amb cURL i la següent comanda (per exemple, per esborrar el jugador Neymar del FC Barcelona):

curl -X DELETE localhost:8080/springrestteamsioc/teams/1/players/3

Executeu la comanda anterior i després consulteu els jugadors del FC Barcelona amb:

curl localhost:8080/springrestteamsioc/teams/1/players/

Si tot ha anat bé, veureu que Neymar ja no és jugador del FC Barcelona:

[
 {"id":1,"name":"Lionel Messi","goals":472,"age":29,"teamId":1},
 {"id":2,"name":"Luis Suarez","goals":100,"age":29,"teamId":1}
]

I amb això ja heu codificat i provat el servei web RESTful que us permet treballar amb els equips i els seus jugadors. El codi final de la classe TeamController és el següent:

  1. package cat.xtec.ioc.controller;
  2.  
  3. import cat.xtec.ioc.domain.Team;
  4. import cat.xtec.ioc.domain.repository.TeamRepository;
  5. import java.util.List;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.web.bind.annotation.PathVariable;
  8. import org.springframework.web.bind.annotation.RequestMapping;
  9. import org.springframework.web.bind.annotation.RequestMethod;
  10. import org.springframework.web.bind.annotation.ResponseBody;
  11. import org.springframework.web.bind.annotation.RestController;
  12.  
  13. @RestController
  14. public class TeamsController {
  15.  
  16. @Autowired
  17. private TeamRepository teamRepository;
  18.  
  19. public TeamsController() {
  20. }
  21.  
  22. public TeamsController(TeamRepository teamRepository) {
  23. this.teamRepository = teamRepository;
  24. }
  25.  
  26. @RequestMapping(method = RequestMethod.GET, value = "/teams")
  27. public @ResponseBody
  28. List<Team> getAll() {
  29. return this.teamRepository.getAll();
  30. }
  31.  
  32. @RequestMapping(value = "/teams/{id}", method = RequestMethod.GET)
  33. public @ResponseBody
  34. Team getById(@PathVariable int id) {
  35. return this.teamRepository.get(id);
  36. }
  37. }

I el de la classe PlayerController és el següent:

  1. package cat.xtec.ioc.controller;
  2.  
  3. import cat.xtec.ioc.domain.Player;
  4. import cat.xtec.ioc.domain.repository.PlayerRepository;
  5. import java.util.List;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.http.HttpStatus;
  8. import org.springframework.http.ResponseEntity;
  9. import org.springframework.web.bind.annotation.PathVariable;
  10. import org.springframework.web.bind.annotation.RequestBody;
  11. import org.springframework.web.bind.annotation.RequestMapping;
  12. import org.springframework.web.bind.annotation.RequestMethod;
  13. import org.springframework.web.bind.annotation.ResponseBody;
  14. import org.springframework.web.bind.annotation.RestController;
  15.  
  16. @RestController
  17. @RequestMapping("/teams/{teamId}/players")
  18. public class PlayerController {
  19.  
  20. @Autowired
  21. private PlayerRepository playerRepository;
  22.  
  23. public PlayerController() {
  24. }
  25.  
  26. public PlayerController(PlayerRepository playerRepository) {
  27. this.playerRepository = playerRepository;
  28. }
  29.  
  30. @RequestMapping(method = RequestMethod.GET)
  31. public @ResponseBody
  32. List<Player> getTeamPlayers(@PathVariable int teamId) {
  33. return this.playerRepository.getByTeam(teamId);
  34. }
  35.  
  36. @RequestMapping(value = "{playerId}", method = RequestMethod.GET)
  37. public @ResponseBody
  38. Player getById(@PathVariable int teamId, @PathVariable int playerId) {
  39. return this.playerRepository.get(teamId, playerId);
  40. }
  41.  
  42. @RequestMapping(method = RequestMethod.POST)
  43. public @ResponseBody
  44. ResponseEntity<Player> create(@PathVariable int teamId, @RequestBody Player player) {
  45. player.setTeamId(teamId);
  46. this.playerRepository.add(player);
  47. return new ResponseEntity<>(player, HttpStatus.CREATED);
  48. }
  49.  
  50. @RequestMapping(method = RequestMethod.PUT)
  51. public @ResponseBody
  52. ResponseEntity<Player> update(@PathVariable int teamId, @RequestBody Player player) {
  53. player.setTeamId(teamId);
  54. this.playerRepository.update(player);
  55. return new ResponseEntity<>(player, HttpStatus.OK);
  56. }
  57.  
  58. @RequestMapping(value = "{playerId}", method = RequestMethod.DELETE)
  59. public @ResponseBody
  60. ResponseEntity<Player> delete(@PathVariable int teamId, @PathVariable int playerId) {
  61. this.playerRepository.delete(teamId, playerId);
  62. return new ResponseEntity<>(HttpStatus.OK);
  63. }
  64. }

Què s'ha après?

En aquest apartat heu vist les bases per al desenvolupament dels serveis web RESTful amb Spring i les heu treballat de forma pràctica mitjançant exemples.

Concretament, heu après:

  • Les nocions bàsiques dels serveis web RESTful amb Spring.
  • Desenvolupar, desplegar i provar un servei web RESTful senzill amb Spring.
  • Fer tests unitaris dels serveis web RESTful amb Spring.
  • Desenvolupar, desplegar i provar un servei web RESTful complex amb Spring que inclou operacions CRUD sobre un recurs.

Per aprofundir en aquests conceptes i veure quins són els conceptes que hi ha darrere de HATEOAS (de l’anglès Hypermedia as the Engine of Application State) us recomanem la realització de l’ activitat associada a aquest apartat.

Anar a la pàgina anterior:
Annexos
Anar a la pàgina següent:
Activitats