REST con Spring

De Dos Ideas.
Revisión del 15:35 22 dic 2010 de Leito (discusión | contribuciones) (Ver también)
Saltar a: navegación, buscar

Spring Framework 3.x trae la posibilidad de crear servicios web REST de manera muy simple.

Un ejemplo

Vamos a realizar una aplicación que responda a las siguientes URL:

/persona/todos   : devuelve todas las personas
/persona/123     : devuelve la persona cuyo id es 123

Tendremos que seguir 3 pasos:

  1. Configurar el servlet de Spring
  2. Crear la clase que atenderá y resolverá estas peticiones
  3. Configurar Spring

El servlet de Spring

Lo primero es configurar el servlet de Spring que se encargará de tomas las peticiones y redireccionarla a las clases correspondientes. La clase que se encarga de esto es org.springframework.web.servlet.DispatcherServlet. Un archivo web.xml de ejemplo sería:

<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

   <display-name>json-con-spring-mvc</display-name>
   <filter>
       <filter-name>characterEncodingFilter</filter-name>
       <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
       <init-param>
           <param-name>encoding</param-name>
           <param-value>UTF-8</param-value>
       </init-param>
       <init-param>
           <param-name>forceEncoding</param-name>
           <param-value>true</param-value>
       </init-param>
   </filter>
   <filter-mapping>
       <filter-name>characterEncodingFilter</filter-name>
       <url-pattern>/*</url-pattern>
   </filter-mapping>
   <servlet>
       <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
       <init-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>

WEB-INF/applicationContext-clasico.xml

           </param-value>
       </init-param>
       <load-on-startup>1</load-on-startup>
   </servlet>
   <servlet-mapping>
       <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
       <url-pattern>/app/*</url-pattern>
   </servlet-mapping>
   <session-config>
       <session-timeout>
           30
       </session-timeout>
   </session-config>
   <welcome-file-list>
       <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>

</web-app>

En este ejemplo, el servlet de Spring queda atendiendo todas las URL que empiecen con "/app".

El controller

La clase que toma las peticiones HTTP es una clase Java anotada con @Controller. Esta clase asocia las URL con métodos. Los objetos que devuelven los métodos son luego procesador por conversores, que representan la respuesta de distinta manera (XML, JSON, etc.)

Para esto, creamos una clase de la siguiente forma:

package com.dosideas.jsonconspringmvc;

import java.util.ArrayList; import java.util.Collection; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;

@Controller @RequestMapping(value="/persona") public class PersonaController {

   @RequestMapping(value="/{id}")
   public @ResponseBody Persona get(@PathVariable Long id) {
       Persona p = new Persona("Invasor " + id, "Zim " + id);
       return p;
   }
   @RequestMapping(value="/todos")
   public @ResponseBody Collection<Persona> getTodos() {
       Collection<Persona> personas = new ArrayList<Persona>();
       for (int i = 0; i < 10; i++) {
           Persona p = new Persona("Invasor " + i, "Zim" + i);
           personas.add(p);
       }
       return personas;
   }

}

El objeto Persona es un POJO que tiene dos atributos: nombre y apellido. Los tags @RequestMapping se encargan de asociar la clase y cada uno de sus métodos con una URL en particular.

La configuración

Lo último que nos queda configurar es Spring con un archivo clásico (el cual es referenciado por el servlet):

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:p="http://www.springframework.org/schema/p"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xmlns:tx="http://www.springframework.org/schema/tx"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:mvc="http://www.springframework.org/schema/mvc"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
      http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
   
   <context:component-scan base-package="com.dosideas.jsonconspringmvc"/>
   <mvc:annotation-driven />

</beans>

El tag mvc:annotation-driven se encarga de configurar automáticamente para emepezar a recibir y responder peticiones usando los controladores encontrados.

Más aún, la configuración predeterminada se encarga de realizar negociación de contenido: la respuesta se formatea de acuerdo a las capacidades del cliente. Por ejemplo, si en el classpath tenemos agregado las librerías de Jackson (un parser JSON), se habilitará el soporte JSON correspondiente. Si están agregadas las librerías de JAXB2, se habilitará el soporte XML.

Probando

Podemos crear una prueba JUnit para acceder a nuestro servicio:

package com.dosideas.jsonconspringmvc;

import java.io.IOException; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpClient; import org.junit.Test; import static org.junit.Assert.*;

public class HttpTest {

   @Test
   public void personaTodosJSON() throws IOException {
       HttpClient httpclient = new HttpClient();
       HttpMethod method = new GetMethod("http://localhost:8084/json-con-spring-mvc/app/persona/todos");
       method.addRequestHeader("accept", "application/json");
       httpclient.executeMethod(method);
       String resultado = method.getResponseBodyAsString();
       System.out.println(resultado);
       assertNotNull(resultado);
       assertTrue(resultado.startsWith("[{\""));
   }
   @Test
   public void personaPorIdJSON() throws IOException {
       HttpClient httpclient = new HttpClient();
       HttpMethod method = new GetMethod("http://localhost:8084/json-con-spring-mvc/app/persona/34421");
       method.addRequestHeader("accept", "application/json");
       httpclient.executeMethod(method);
       String resultado = method.getResponseBodyAsString();
       System.out.println(resultado);
       assertNotNull(resultado);
       assertTrue(resultado.startsWith("{\""));
       assertTrue(resultado.contains("34421"));
   }
   @Test
   public void personaPorIdXML() throws IOException {
       HttpClient httpclient = new HttpClient();
       HttpMethod method = new GetMethod("http://localhost:8084/json-con-spring-mvc/app/persona/34421");
       method.addRequestHeader("accept", "application/xml");
       httpclient.executeMethod(method);
       String resultado = method.getResponseBodyAsString();
       System.out.println(resultado);
       assertNotNull(resultado);
       assertTrue(resultado.startsWith("<?xml version=\"1.0\""));
       assertTrue(resultado.contains("34421"));
   }

}

Ver también