Serveis web RESTful amb Spring. Consumint serveis web
Explicarem, mitjançant exemples, com podem consumir serveis web RESTful remots amb diferents tipus de clients.
Es poden provar els serveis RESTful amb qualsevol eina que permeti fer peticions HTTP.
La primera eina en la qual pensaríem tots és un navegador web (Microsoft Internet Explorer, Google Chrome, Mozilla Firefox, etc.). El problema és que els navegadors, per defecte, tan sols poden fer peticions GET
i peticions POST
. Si voleu fer peticions d’un altre tipus (PUT
, DELETE
, HEAD
) caldrà instal·lar algun plugin al navegador que us ho permeti (Postman és un possible plugin per a Google Chrome, però n’hi ha molts).
Una altra opció és emprar la utilitat cURL, que us permet fer tot tipus de peticions HTTP per línia de comandes.
Si el que volem és consumir serveis web RESTful des de Java ho podem fer amb l’API de baix nivell java.net.HttpURLConnection
o alguna llibreria propietària que us fes la vida una mica més fàcil.
Una opció és utilitzar la classe RestTemplate
, que proporciona Spring, o bé l’API client de JAX-RS; les dues opcions permeten fer tota mena de peticions HTTP als serveis web RESTful remots de forma fàcil.
Spring proporciona la classe RestTemplate
per simplificar la comunicació HTTP entre el client i el servidor. RestTemplate
permet fer tota mena de peticions HTTP als serveis web RESTful remots de forma fàcil.
RestTemplate
es pot utilitzar per fer tests d’integració per provar els serveis RESTful, estiguin o no desenvolupats amb Spring.
AJAX és un conjunt de tècniques de desenvolupament d’aplicacions web que permeten crear aplicacions web client asíncrones que cada vegada s’utilitzen més per accedir a serveis web RESTful, tant des d’aplicacions web com des d’aplicacions mòbils.
Un client Java per al servei web RESTful "'Hello, World!!!'"
Veurem com consumir serveis web RESTful des d’una aplicació Java stand-alone utilitzant la classe RestTemplate
que proporciona Spring per fer-ho.
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 “Springresthellojavaclientioc” en l’estat inicial d’aquest apartat en l’enllaç que trobareu als annexos de la unitat i importeu-lo a NetBeans.
package cat.xtec.ioc.springresthelloioc.controller; import cat.xtec.ioc.springresthelloioc.domain.Greeting; import java.util.concurrent.atomic.AtomicLong; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class GreetingController { private final AtomicLong counter = new AtomicLong(); @RequestMapping(method = RequestMethod.GET, value = "/hello") return new Greeting(counter.incrementAndGet(), } }
Aquest és un servei web RESTful desenvolupat amb Spring que respon a peticions GET
a l’URI /hello amb “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:
{"id":1,"content":" Hello, World!!!"}
I a les peticions GET
a /hello?name=User amb:
{"id":1,"content":"Hello, User!"}
Un cop importat el projecte cal que modifiqueu el pom.xml per afegir les dependències cap a Jackson per tal que Spring pugui fer la transformació de dades de JSON a objecte Java. Per fer-ho, afegiu les següents línies al fitxer pom.xml:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.5.3</version> </dependency>
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 client Spring.
Creació del client Java 'stand-alone'
El primer que necessitem és una classe Java que representarà el model del domini a la part client i que Spring construirà a partir del missatge JSON retornat pel servei web.
Fixeu-vos que en el nostre cas es podria haver utilitzat per fer això la classe Greeting
que teniu al paquet cat.xtec.ioc.springresthelloioc.domain
, i ens estalviaríem de crear-ne una de nova. En crearem una, ja que l’habitual és que el client i el servei web no estiguin al mateix projecte; nosaltres els hem posat al mateix projecte per simplicitat.
Creeu, doncs, la classe Java que modelarà la resposta del servei web al paquet cat.xtec.ioc.springresthelloioc.client
i l’anomeneu GreetingClient
:
package cat.xtec.ioc.springresthelloioc.client; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class GreetingClient { private long id; public long getId() { return id; } public void setId(long id) { this.id = id; } return content; } this.content = content; } @Override return "Greeting{" + "id='" + id + '\'' + ", content=" + content + '}'; } }
Es tracta d’una classe Java normal amb una propietat per a cada un dels camps que conté la salutació que torna el servei web. L’única particularitat és l’anotació @JsonIgnoreProperties(ignoreUnknown = true)
per indicar que en la transformació de JSON a objecte Java ignori les propietats que no tinguin un mapatge definit.
Creeu la classe Java que farà de client al paquet cat.xtec.ioc.springresthelloioc.client
i l’anomeneu HelloWorldClient
:
package cat.xtec.ioc.springresthelloioc.client; import org.springframework.web.client.RestTemplate; public class HelloWorldClient { RestTemplate restTemplate = new RestTemplate(); GreetingClient greeting = restTemplate.getForObject("http://localhost:8080/springresthellojavaclientioc/hello", GreetingClient.class); greeting = restTemplate.getForObject("http://localhost:8080/springresthellojavaclientioc/hello?name=User", GreetingClient.class); } }
Com veieu, el client és molt simple: creem un objecte de tipus RestTemplate
i cridem el mètode getForObject
amb l’URL del servei web i la classe en la qual ha de convertir el missatge JSON rebut.
Aquesta és només una de les maneres d’utilitzar la classe RestTemplate
; n’hi ha moltes més que us permetran fer crides amb tot el conjunt de verbs HTTP, passar paràmetres JSON, recuperar tota la informació de la resposta rebuda, etc. Per veure totes les opcions que us permet la classe RestTemplate
podeu consultar la seva API en la següent adreça: bit.ly/2nywYZy.
Ara ja només ens queda desplegar el servei web i executar el client per veure si es comporta com volem.
Desplegament del servei web i prova amb el client Java
Desplegueu el servei web com a part de l’aplicació Java EE que el conté fent Clean and Build i després Run a NetBeans.
Comproveu que el servei web està desplegat correctament accedint a l’URL localhost:8080/springresthellojavaclientioc/hello amb un navegador. El servei web us ha de tornar la salutació “Hello, World!!!” en format JSON.
Si tot és correcte ja podem executar el client; per fer-ho, poseu-vos damunt de la classe HelloWorldClient
i feu Run File a NetBeans i veureu la salutació ”Hello, World!!!” i “Hello, User” a la consola de sortida:
Greeting{id='1', content=Hello, World!!!} Greeting{id='2', content=Hello, User} ------------------------------------------------------------------------ BUILD SUCCESS ------------------------------------------------------------------------
Tests d’integració per al servei web RESTful '"Hello, World!!!'"
Vegem com fer tests d’integració d’un servei web RESTful amb JUnit i la classe RestTemplate
.
El primer que farem serà desplegar a Glassfish el servei web REST que volem provar i després crearem el conjunt de tests d’integració que faran peticions HTTP al servei web.
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.
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 “Springtestintresthelloioc” de l’enllaç que trobareu als annexos de la unitat i importeu-lo a NetBeans.
package cat.xtec.ioc.springresthelloioc.controller; import cat.xtec.ioc.springresthelloioc.domain.Greeting; import java.util.concurrent.atomic.AtomicLong; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class GreetingController { private final AtomicLong counter = new AtomicLong(); @RequestMapping(method = RequestMethod.GET, value = "/hello") return new Greeting(counter.incrementAndGet(), } }
Aquest és un servei web RESTful desenvolupat amb Spring que respon a peticions GET a l’URI /hello amb “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"}
Crearem els tests d’integració dins el mateix projecte que conté el codi del servei web que volem provar. Una altra opció, igualment vàlida, seria crear un nou projecte i posar-hi només els tests.
JUnit
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.
Un cop importat el projecte cal que modifiqueu el pom.xml per afegir les dependències cap a Jackson per tal que Spring pugui fer la transformació de dades de JSON a objecte Java i a JUnit 4. Per fer-ho, afegiu les següent línies al fitxer pom.xml:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.5.3</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> <type>jar</type> </dependency>
Si recarregueu el pom.xml fent clic amb el botó dret damunt el nom del projecte i premeu l’opció Reload POM del menú contextual ja tindreu el projecte configurat i llest per començar a crear el client amb Spring.
Primer desplegueu el servei web com a part de l’aplicació Java EE que el conté fent Clean and Build i després Run a NetBeans, i comproveu que s’ha desplegat correctament accedint a l’URL localhost:8080/springtestintresthelloioc/hello amb un navegador. El servei web us ha de tornar la salutació “Hello, World!!!” en format JSON:
{"id":1,"content":" Hello, World!!!"}
Noteu que l’identificador retornat anirà canviant en funció del número de petició.
Creació i execució dels tests d’integració
Ara toca començar a crear els tests d’integració per provar el servei web RESTful.
El primer que necessitem és una classe Java que representarà el model del domini i que Spring construirà a partir del missatge JSON retornat pel servei web.
Fixeu-vos que per fer això es podria haver utilitzat la classe Greeting
que teniu al paquet cat.xtec.ioc.springresthelloioc.domain
i ens estalviaríem de crear-ne una de nova (tan sols li hauríeu d’haver afegit un constructor sense paràmetres).
Creeu un nou paquet dins de Test Packages anomenat, per exemple, cat.xtec.ioc.springresthelloioc.controller
, i creeu-hi una classe Java anomenada GreetingTest
:
package cat.xtec.ioc.springresthelloioc.controller; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class GreetingTest { private long id; public long getId() { return id; } public void setId(long id) { this.id = id; } return content; } this.content = content; } @Override return "Greeting{" + "id='" + id + '\'' + ", content=" + content + '}'; } }
Es tracta d’una classe Java normal amb una propietat per a cada un dels camps que conté la salutació que torna el servei web. L’única particularitat és l’anotació @JsonIgnoreProperties(ignoreUnknown = true)
per indicar que en la transformació de JSON a objecte Java ignori les propietats que no tinguin un mapatge definit.
Creeu ara al paquet cat.xtec.ioc.springresthelloioc.controller
una classe Java anomenada GreetingControllerTest
:
package cat.xtec.ioc.springresthelloioc.controller; import javax.ws.rs.core.MediaType; import static org.junit.Assert.assertEquals; import org.junit.Test; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; public class GreetingControllerTest { }
Estructura dels tests
Un dels patrons més utilitzats a l’hora d’estructurar el codi d’un test és l’anomenat AAA (de l’anglès Arrange-Act-Assert), amb el qual els tests sempre tindran una fase de preparació (Arrange), una d’execució (Act) i una de verificació de resultats (Assert).
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:
@Test public void greetingShouldReturnHelloWorldWithoutName() { // Arrange RestTemplate restTemplate = new RestTemplate(); // Act GreetingTest greeting = restTemplate.getForObject(uri, GreetingTest.class); // Assert assertEquals("Hello, World!!!", greeting.getContent()); }
Hem anotat el mètode greetingShouldReturnHelloWorldWithoutName
amb l’anotació @Test
per indicar que es tracta d’un mètode de test; en la fase de preparació simplement creem l’objecte RestTemplate
per enviar les peticions HTTP al servei web RESTful, després executem la petició cridant el mètode getForObject
i, finalment, verifiquem que la salutació retornada coincideix amb la que esperem.
Ja podem executar el test que hem creat fent Test File al menú contextual de la classe de Test, i si tot ha anat bé veureu el resultat de l’execució correcta dels tests a la finestra de Test.
Per mostrar alguna altra capacitat de la classe RestTemplate
farem un altre test que comprovarà que el codi HTTP de retorn és un 200 (OK) i que el content type sigui JSON.
Per fer-ho, creeu un mètode anomenat greetingShouldReturnOKAndJSON
dins la classe GreetingControllerTest
amb el següent codi:
@Test public void greetingShouldReturnOKAndJSON () { // Arrange RestTemplate restTemplate = new RestTemplate(); // Act ResponseEntity<GreetingTest> response = restTemplate.getForEntity(uri, GreetingTest.class); // Assert assertEquals(HttpStatus.OK, response.getStatusCode()); assertEquals(MediaType.APPLICATION_JSON, response.getHeaders().getContentType().getType() + "/" + response.getHeaders().getContentType().getSubtype()); }
Executeu-lo de la mateixa manera i comproveu que el test s’executa correctament.
El darrer 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:
@Test public void greetingShouldReturnHelloNameWithName() { // Arrange RestTemplate restTemplate = new RestTemplate(); // Act GreetingTest greeting = restTemplate.getForObject(uri + "?name=User", GreetingTest.class); // Assert assertEquals("Hello, User", greeting.getContent()); }
El codi és molt similar al primer test; executeu-lo de la mateixa manera i comproveu que el test s’executa correctament.
Aquests tres exemples us donen la base per tal que pugueu fer tots els tests d’integració que se us acudeixin i així tenir els serveis web RESTful totalment provats!
L’ús de RestTemplate
és l’opció que proporciona Spring; els mateixos tests d’integració els podríeu haver escrit amb qualsevol altre bastiment que permeti fer peticions HTTP. Una opció perfectament vàlida hauria estat utilitzar JAX-RS, l’API client per consumir serveis web RESTful que proporciona Java EE.
Noteu que, a part dels tests d’integració, Spring també us permet fer tests unitaris per provar els serveis web RESTful.
Un client JavaScript per al servei web RESTful "'Hello, World!!!'"
Veurem com consumir serveis web RESTful des d’una aplicació web amb peticions AJAX de JavaScript. Utilitzarem Angular JS com a bastiment JavaScript per fer les peticions AJAX.
AJAX (de l’anglès Asynchronous JavaScript And XML) és una tècnica de desenvolupament que permet fer les pàgines web d’una aplicació web interactives amb JavaScript.
AJAX és un conjunt de tècniques (vegeu la figura) de desenvolupament d’aplicacions web que permeten crear aplicacions web client asíncrones. Amb AJAX, la part client de les aplicacions web pot enviar i recuperar informació del servidor de forma asíncrona (en background) sense interferir en com es mostra i com es comporta la pàgina. Aquesta forma de desacoblar la capa d’intercanvi de dades de la capa de presentació permet a les pàgines web, i, per extensió, a les aplicacions web, intercanviar contingut dinàmicament amb el servidor sense haver de recarregar tota la pàgina. Tot i que inicialment estava centrat en XML, cada vegada més el format de les dades intercanviades tendeix més a ser JSON, ja que aquest és un format nadiu per a les dades en JavaScript.
Objectiu de l'exemple
L’objectiu d’aquest exemple no és aprofundir en el coneixement d’Angular JS, sinó que només pretén mostrar com es pot fer servir AJAX com a client per consumir serveis web RESTful. Si voleu aprofundir en el coneixement d’Angular JS ho podeu fer a l’URL angularjs.org.
Les aplicacions que fan servir AJAX segueixen, en molta mesura, els principis de disseny inherents a l’arquitectura d’aplicacions REST. Podem veure cada una de les peticions AJAX com una petició a un servei REST que tornarà les dades en un format determinat, moltes vegades JSON, per tal que l’aplicació web les tracti i mostri a l’usuari sense necessitat de recarregar tota la pàgina.
Aquestes característiques fan d’AJAX una de les formes més utilitzades a l’hora de consumir serveis web RESTful des de pàgines o aplicacions web.
Actualment hi ha multitud de bastiments JavaScript que proporcionen, entre moltes altres coses, la possibilitat de fer peticions AJAX. Potser el més popular des dels seus inicis és jQuery.
Angular JS és un altre bastiment JavaScript que, entre moltes altres coses, proporciona capacitats i ajudes per fer peticions AJAX a serveis web RESTful.
A l’exemple veurem un client Angular JS que consumirà amb AJAX el servei web RESTful de salutacions.
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 “Springresthelloangularclientioc” en l’estat inicial d’aquest apartat en l’enllaç que trobareu als annexos de la unitat i importeu-lo a NetBeans.
package cat.xtec.ioc.springresthelloioc.controller; import cat.xtec.ioc.springresthelloioc.domain.Greeting; import java.util.concurrent.atomic.AtomicLong; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class GreetingController { private final AtomicLong counter = new AtomicLong(); @RequestMapping(method = RequestMethod.GET, value = "/hello") return new Greeting(counter.incrementAndGet(), } }
Aquest servei web és un servei web RESTful desenvolupat amb Spring que respon a peticions GET
a l’URI /hello amb “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!"}
Creació del client JavaScrip amb Angular JS
El primer que necessitem és crear un mòdul controlador Angular JS que consumeixi el servei web. Per fer-ho, creeu un fitxer anomenat helloangular.js a Web Pages/static amb el següent contingut:
angular.module('demo', []) .controller('Hello', function ($scope, $http) { $http.get('http://localhost:8080/springresthelloangularclientioc/hello'). then(function (response) { $scope.greeting = response.data; }); });
Aquest mòdul controlador, anomenat Hello, es representa com una funció JavaScript a la qual se li passa com a paràmetres els components scope
i http
. La funció fa servir el component http
per fer una crida al servei RESTful a /hello.
Si la crida té èxit, s’assignarà el JSON retornat des del servei web la variable scope.greeting
amb la creació d’un objecte JavaScript que representarà el model. L’establiment d’aquest objecte al model fa que Angular el pugui utilitzar al DOM de la pàgina que fa la sol·licitud i mostrar el seu contingut a l’usuari.
Un cop tenim el controlador ens cal crear la pàgina HTML que el carregarà al navegador de l’usuari. Per fer-ho, creeu un fitxer anomenat helloangular.html a Web Pages/static amb el següent contingut:
A la secció head de la pàgina hi posem aquestes dues etiquetes:
La primera descarrega la llibreria angular
del CDN (de l’anglès Content Delivery Network) per tal que no l’haguem d’incorporar al projecte, i el segon carrega el codi JavaScript del controlador que hem creat (helloangular.js).
Angular JS habilita un conjunt d’etiquetes pròpies que podrem utilitzar a les pàgines HTML. A l’exemple fem servir l’atribut ng-app
per indicar que la pàgina és una aplicació Angular JS i l’etiqueta ng-controller
per indicar que el controlador de la pàgina serà el controlador Hello
definit al fitxer JavaScript helloangular.js.
Finalment, la pàgina accedeix a l’objecte del model greeting
que ens ha deixat el controlador i mostra l’identificador i la salutació.
Ara ja sols ens queda desplegar el servei web i el client per veure si es comporta com volem.
Desplegament del servei web i prova del client Angular
Desplegueu el servei web com a part de l’aplicació Java EE que el conté fent Clean and Build i després Run a NetBeans.
Comproveu que el servei web està desplegat correctament accedint a l’URL localhost:8080/springresthelloangularclientioc/hello amb un navegador. El servei web us ha de tornar la salutació “Hello, World!!!” en format JSON.
Si tot és correcte ja podem provar el client Angular JS accedint a l’URL localhost:8080/springresthelloangularclientioc/static/helloangular.html amb un navegador, i veureu la salutació ”Hello, World!!!” tornada pel servei web (vegeu la figura).
Què s'ha après?
En aquest apartat heu vist les bases per al desenvolupament de clients Java i JavaScript que accedeixin a serveis web RESTful, i els heu treballat de forma pràctica mitjançant exemples.
Concretament, heu après a:
- Desenvolupar i provar un client Java stand-alone que us permeti consultar un servei web RESTful mitjançant la classe
RestTemplate
. - Fer tests d’integració amb
RestTemplate
que us permetin provar els serveis web RESTful. - Fer crides AJAX a serveis web RESTful.
Per veure com habilitar sol·licituds Cross-Origin per un servei web RESTful us recomanem la realització de l’activitat associada a aquest apartat.