Servicio Web Restful con Spring Boot

De ChuWiki
Saltar a: navegación, buscar

Veamos un ejemplo rápido de cómo crear con Spring Boot una pequeña aplicación que ofrezca un servicio web Restful. Puedes ver todas las clases de este ejemplo en Github. El proyecto en Github tiene más ejemplos que el de servicio Web Restful, por lo que sólo debes fijarte en las clases del paquete com.chuidiang.examples.rest

Luego puedes ver cómo hacer el cliente Rest con Spring (boot o sin boot).

Dependencias

Vamos a usar Categoría:Maven para el proyecto y así poder bajarnos fácilmente las dependencias necesarias de Spring Boot. La única dependencia que necesitamos para este ejemplo es spring-boot-starter-web

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<version>1.3.3.RELEASE</version>
		</dependency>

El resto del fichero pom.xml no tiene nada especial.

Algunas clases para datos

Es habitual en un servicio web restful que podamos consultar una lista de datos, borrarlos, modificarlos y añadir nuevos. Crearemos primero unas clases que nos permitan hacer esto. Como no tienen nada especial, no nos metemos en detalles con ellas. Simplemente mencionarlas

  • Greeting.java es una clase con datos, sólo un par de atributos: un long y un String con sus constructores, setter y getter correspondientes. Es importante que tenga un constructor por defecto.
  • Data.java mantiene un ArrayList de Greeting y métodos para poder añadir, borrar, obtener y modificar los Greeting del ArrayList. Como vamos a usar Spring, a esta clase le ponemos la siguiente anotación
import org.springframework.stereotype.Component;

@Component
public class Data {
   ...
}

De esta forma podremos "inyectar" una instancia de esta clase en otras clases usando la anotación @Autowired, como veremos más adelante.

El Web Service Controller

Vamos ahora a meternos en el meollo del asunto. Para nuestro Web Service Restful, necesitamos una clase con los métodos que queramos ofrecer al exterior. Esta clase es GreetingController.java.

La clase viene anotada con @RestController, de esta forma Spring sabrá que debe ofrecer esta clase como Web Service Restful.

@RestController
public class GreetingController {
   ....
}

Esta clase necesita la clase Data que mencionamos antes y que es la que contiene la lista de Greeting y los métodos para poder añadir, modificar y borrar elementos. Aprovechando la inyección de dependencias de Spring, habíamos anotado la clase Data con la anotación @Component. Esto nos permite en la clase GreetingController poner el siguiente atributo

@RestController
public class GreetingController {

   @Autowired
   private Data data;

   ....
}

La anotación @Autowired hace que Spring cree una instancia de Data y la ponga como atributo de nuestra clase GreetingController. Queda de esta forma inicializado con un valor el atributo data.

Obtener lista

En un servicio web Restful, suele ser habitual utilizar una URL estilo http://<servidor>:<puerto>/greeeting para obtener la lista de Greeting. En un servicio Restful, esta petición para obetner datos suele ser de tipo GET. Así que el método de GreetingController para esto quedará así

   @RequestMapping(method = RequestMethod.GET, path = "/greeting")
   public List<Greeting> greeting() {
      return data.getGreetings();
   }

El método simplemente devuelve una lista de List<Greeting> utilizando el atributo data. Veamos las anotaciones, que son la parte donde está la magia.

Hemos anotado el método con @RequestMapping para indicar que debe atender a peticiones GET con method = RequestMethod.GET y a la URL path = "/greeting". Listo, no hay que hacer más. Esto, cuando lo arranquemos y consultemos esa URL con un cliente Web Service REST (el mismo navegador web nos vale) nos proporcionará en formato JSON la lista de Greeting.

Obtener un elemento

Para obtener un Greeting concreto, suele ser habitual usar petición GET a una URL estilo http://<servidor>:<puerto>/greeting/<id> donde id es algún tipo de identificador del Greeting que queremos obtener. En nuestro ejemplo, ese id será el índice del ArrayList. El código para el método puede ser como el siguiente

   @RequestMapping(method = RequestMethod.GET, path = "/greeting/{id}")
   public Greeting greeting(@PathVariable Integer id)
         throws NoSuchRequestHandlingMethodException {

      try {

         Greeting greeting = data.getGreeting(id);
         return greeting;

      } catch (IndexOutOfBoundsException e) {
         throw new NoSuchRequestHandlingMethodException("greeting",
               GreetingController.class);
      }

   }

Hay varios detalles interesantes aquí, vamos a ir viéndolos.

Si on existe un elemento con ese id (en nuestro caso, sería que nos pidieran un índice fuera del rango del ArrayList que hay dentro de data), deberíamos devolver un error 404 al navegador o al cliente del servicio. Por ello, capturamos la excepción IndexOutOfBoundsException y la relanzamos como NoSuchRequestHandlingMethodException, que es una excepción específica de Spring y que devuelve el error 404.

En cuanto a las anotaciones, ya conocemos @RequestMapping(method = RequestMethod.GET). En la parte de path hemos añadido, sin embargo, un {id} al path. Esto indica a Spring que vale cualquier path estilo /greeting/id siendo id cualquier cosa. Y que tenemos interés además en saber qué es esa cosa que han escrito ahí, es decir, el id.

¿Cómo recogemos ese id?. Pues poniéndolo como parámetro en nuestro método y anotándolo con @PathVariable. Esto indica a Spring que debe rellenar nuestro parámetro id con lo que el cliente haya escrito en la URL en el sitio donde va {id}.

El resto es sencillo, pedimos a data el elemento en la posición id y lo devolvemos. Si salta una excepción IndexOutOfBounds, la relanzamos como NoSuchRequestHandlingMethodException

Crear un nuevo elemento

Para crear un nuevo elemento en una petición REST, suele ser habitual usar el método POST, enviando los valores del nuevo elemento como parámetros de la petición. La URL no suele llevar el identificador, ya que en las peticiones POST, suele ser cosa del servidor decidir la URL concreta en la que se alojará el elemento cuando sea creado, es decir, en este caso, asignarle un identificador.

El código java para este tipo de petición podría quedar así

   @RequestMapping(method = RequestMethod.POST, path = "/greeting")
   public Greeting add(@RequestParam(value = "content") String content) {
      return data.addGreeting(content);
   }

En el código en sí no hay nada especial, recibimos como parámetro un String content y llamamos al método data.addGreeting(content) que se encargará de crear un nuevo Greeting y meterlo en la lista. Este método devuelve el Greeting creado que a su vez devolveremos en el método del web service.

Miremos las anotaciones. La anotación que usamos en el método @RequestMapping(method = RequestMethod.POST, path = "/greeting") no tienen nada que no hayamos comentado ya. Admite peticiones POST y la URL será http://<servidor>:<puerto>/greeting.

En cuanto al parámetro content del método, lo hemos anotado con @RequestParam, ya que al ser una llamada POST, el parámetro vendrá dentro de la petición y no como parte de la URL, como pasaba en el caso de obtener un elemento de la lista. Como pueden venir varios parámetros dentro de la petición, debemos indicar cual es el que nos interesa en concreto. En este caso hemos decidido poner value = "content", por lo que el cliente que haga la petición tendrá que pone el texto que quiera en un parámetro que se llame content. Dicho de otra forma, si la llamada a este método la hiciera un formulario HTML, el método de envío debería ser POST y el nombre del campo de texto que rellena el usuario debería llamarse content.

Borrar un elemento

Para borrar un elemento, usaremos una llamada HTPP de tipo DELETE. Para identificar el elemento a borrar, lo haremos con la URL, estilo http://<servidor>:<puerto>/greeting/<id>, donde id es el identificador del elemento a borrar. El código java puede ser como el siguiente

   @RequestMapping(method = RequestMethod.DELETE, path = "/greeting/{id}")
   public void delete(@PathVariable Integer id)
         throws NoSuchRequestHandlingMethodException {
      try {
         data.removeGreeting(id);
      } catch (IndexOutOfBoundsException e) {
         throw new NoSuchRequestHandlingMethodException("greeting",
               GreetingController.class);
      }
   }

Nada especial en este método, es muy similar el método GET para obtener un elemento. Unicamente se usa RequestMethod.DELETE y el código por dentro llama a data.removeGreeting(id);. Al igual que en el método GET, capturamos la excepción IndexOutOfBoundsException, por si nos piden borrar un elemento que no existe, y la relanzamos como NoSuchRequestHandlingMethodException para devolver un error 404 a nuestro cliente.

Modificar un elemento

Para modificar un elemento se suele usar una petición HTTP PUT. Si el elemento no existe, suele ser habitual que la petición PUT lo cree. Una petición PUT, estrictamente hablando, más que para modificar un elemento, sirve para poner un elemento en una URL concreta, modificando al que estuviera en esa URL si existe, o creándolo si no existiera. Sin embargo, suele ser habitual usarlo sólo para modificar, ya que la URL concreta suele llevar el identificador del elemento y asignar un nuevo identificador suele ser cosa del servidor y no del cliente.

El código java puede ser este

   @RequestMapping(method = RequestMethod.PUT, path = "/greeting/{id}")
   public Greeting add(@PathVariable Integer id,
         @RequestBody Greeting greeting)
               throws NoSuchRequestHandlingMethodException {
      try {
         return data.updateGreeting(id, greeting);
      } catch (IndexOutOfBoundsException e) {
         throw new NoSuchRequestHandlingMethodException("greeting",
               GreetingController.class);
      }
   }

Bien, es muy parecido al método GET de un elemento. Hemos puesto method = RequestMethod.PUT. La URL será estilo http://<servidor>:<puerto>/greeting/<id> donde id es el identificador del elemento que queremos cambiar. El elemento lo pasaremos en formato JSON, estilo así

{"id":33,"content":"Perico de los Palotes"}

y debe ir como contenido de la petición HTTP. Para poder hacer esto no nos vale el navegador web normal, necesitaremos una aplicación específica que nos permita hacer peticiones PUT y poner el contenido (body) de la petición.

En nuestro código, ponemos como parámetro directamente un Greeting greeting, pero lo anotamos con @RequestBody para indicarle a Spring que debe construir Greeting a partir del contenido (body) de la petición, que estará en formato JSON. Este es el motivo, como mencionamos muy al principio, que Greeting necesita un constructor sin parámetros, para que Spring sepa construir aquí una instancia de esta clase, rellenándola después con los datos del contenido de la petición y pasárnosla después.

Aunque estrictamente hablando es incorrecto, hemos decidido en este código no dejar crear elementos nuevos. Si la llamada a data.updateGreeting(id, greeting); hace saltar un IndexOutOfBoundsException, es que ese elemento no existe previamente y relanzamos el error. En una petición PUT 100% correcta, deberíamos crear ese elemento en la posición que nos han indicado.

Listo. Ahora puedes ver cliente Rest con Spring (boot o sin boot).