Spring mvc. Servicios Rest respondiendo en Json o XML
0. Índice de contenidos.
- 1. Introducción
- 2. Entorno
- 3. Configurando los servicios Rest con Spring mvc
- 4. Probando los distintos tipos de respuesta
- 5. Conclusiones
- 6. Información sobre el autor
1. Introducción
En este tutorial vamos a ver las configuraciones necesarias para que un servicio Rest, bajo Spring mvc devuelva XML o Json en función del accept de la cabecera de la petición.
Para devolver XML o Json tenemos varias opciones pero la más elegante me parece tener un solo servicio Rest mapeado en una sola URL, y que en función de lo que quiera el cliente ( accept en la cabecera de la petición ) se sirva una cosa u otra.
2. Entorno
- Hardware: Portátil MacBook Pro 15′ (2.0 GHz Intel i7, 8GB DDR3 SDRAM, 500GB HDD).
- AMD Radeon HD 6490M 256 MB
- Sistema Operativo: Mac OS X Snow Leopard 10.6.7
- Software relacionado con el tutorial: Eclipse Helios, Spring 3.1.1.RELEASE, Apache Tomcat 7
3. Configurando los servicios Rest con Spring mvc
Lo primero de todo es configurar el web.xml.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>rest</display-name> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <description>Spring configuration file</description> <param-name>contextConfigLocation</param-name> <param-value> classpath:applicationContext-model.xml, WEB-INF/applicationContext-servlet.xml </param-value> </context-param> <context-param> <param-name>webAppRootKey</param-name> <param-value>rest</param-value> </context-param> <servlet> <servlet-name>applicationContext</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>applicationContext</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping> |
- En la línea 4 estamos declarando el listener de Spring responsable de levantarnos nuestros contextos.
- En las líneas 7-14 definimos los applicationContext que queremos levantar. Luego los veremos más en profundidad.
-
En las líneas 21-30 declaramos en Servlet de forma estándar. Según la declaración que hacemos la configuración del Servlet se espera en un archivo con el nombre «applicationContext-servlet.xml», es decir, nombre del Servlet seguido de -servlet.xml (estándar). Lo estamos mapeando contra /rest/*, es decir
todas las llamadas del tipo localhost:8080/rest/rest/* serán tratadas por este Servlet.
Vamos a ver ahora la configuración del Servlet, que la tenemos como ya hemos dicho en applicationContext-servlet.xml
¿Sencillo verdad? Aún así siempre me gusta explicarlo todo para que quede clarito y por si alguien tiene alguna duda.
- En la línea 10 indicamos a Spring que vamos a utilizar anotaciones, y que queremos que nos instancie las clases que tengamos anotadas, por ejemplo, con @Controller.
- En la línea 12 le decimos a partir de que paquete tiene que escanear en busca de clases anotadas.
A continuación vamos a crearnos un controlador con el soporte de Spring mvc para que gestione las peticiones. Lo vemos a continuación.
1 2 3 4 5 6 7 8 9 10 11 |
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd"> <mvc:annotation-driven/> <context:component-scan base-package="com.autentia.tutoriales.rest.*" /> </beans> |
Como veis hemos declarado un controlador al que le llegarán las peticiones del tipo http://localhost:8080/rest/rest/rrhh/. Aquí podemos meter una serie de métodos relacionadas con la gestión de personal que normalmente llamarán a servicios que tengamos alojados en nuestro modelo y devolverán una respuesta.
Si lo que queremos es una respuesta Json o XML dependiente de la cabecera de la petición no podemos utilizar el atributo «produces» de la anotación @RequestMapping para indicar que este método devuelve application/json ó application/xml. Lo que tenemos que hacer es lo siguiente:
- 1º. Insertamos la siguiente dependencia en el pom de nuestro módulo web donde tenemos definido el controlador. Es necesaria para poder ofrecer la respuesta en Json.
- 2º. En nuestra entidad Person, tendremos que definir los elementos del XML que queramos que aparezan. Lo vemos a continuación.
1 2 3 4 5 |
<dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.6.4</version> </dependency> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package com.autentia.tutoriales.entities; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement @Entity public class Person { @Id @GeneratedValue private int id; @XmlElement private final String name; public Person(String name) { this.name = name; } public String getName() { return name; } public int getId() { return id; } } |
Como podeis ver hemos anotado la clase como raíz del XML con @XmlRootElement y los atributos que queremos que aparezan en el XML los anotamos con @XmlElement
Esta clase la tengo en otro módulo que es el que he definido como model en el proyecto. Si os acordais en el web.xml definiamos el applicationContext-model que corresponde precisamente a este módulo. Os lo pego a continuación para tener todas las piezas del puzzle.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"> <context:annotation-config /> <!-- | Packages to scan, looking for @Entity annotation --> <bean id="entitiesPackagesToScan" class="java.util.ArrayList"> <constructor-arg index="0"> <list> <value>com.autentia.tutoriales.entities</value> </list> </constructor-arg> </bean> </beans> |
4. Probando los distintos tipos de respuesta
Nos vamos a crear un cliente sencillito en HTML que haga 2 peticiones al servidor, una que tenga en el accept de la cabecera «application/json» y la otra «application/xml». El código fuente del cliente os lo pongo a continuación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<html> <head> <script type="text/javascript" src="/rest/js/jquery-1.6.1.min.js"></script> <script type="text/javascript" src="/rest/js/index.js"></script> </head> <body> TUTORIAL SERVICIOS REST JSON/XML <p> <a href="#" onclick="jsonPerson()"><strong>JSON</strong></a> </p> <p> <strong>XML</strong> </p> </body> </html> |
Estamos incluyendo 2 javascripts, el primero es jquery,
que os lo podeis bajar de: http://code.jquery.com/jquery-1.7.2.min.js
El segundo es un javascript muy cortito cuyo código os pongo a continuación :
1 2 3 4 |
function jsonPerson() { var url = "http://localhost:8080/rest/rest/rrhh/person/"; $.getJSON(url,null); } |
Si pinchamos en JSON, podemos ver en los detalles de la petición el Accept
Si ahora nos vamos a la respuesta tendremos lo siguiente:
{«name»:»Respuesta en formato dependiente del header de la petición»,»id»:0}
que es el Json que representa al objeto que hemos creado.
Si repetimos el proceso pulsando en XML en la respuesta nos vendrá lo siguiente:
1 2 3 4 |
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <person> <name>Respuesta en formato dependiente del header de la petición</name> </person> |
Por último comentar que si quisieramos forzar un formato en concreto
podríamos anotar el método de la siguiente forma:
@RequestMapping(value = «/person/», method = RequestMethod.GET, produces=»application/json»). Esto devolvería siempre json.
5. Conclusiones
Me parece muy útil tener un servicio que responda en distintos formatos según lo que acepte el cliente y a nivel de código queda muy limpio al tenerlo en un solo método. Espero que os haya sido útil el tutorial. Un saludo.
6. Información sobre el autor
Alberto Barranco Ramón es Ingeniero Técnico en Informática de Gestión y Graduado en Ingeniería del Software por la Universidad Politécnica de Madrid
Mail: abarranco@autentia.com.
Twitter: @barrancoalberto
Autentia Real Business Solutions S.L. – «Soporte a Desarrollo».
Hola Alberto,
Bueno tutorial, un ejemplo muy sencillo con el que verlo muy claro. Me ha gustado ver lo fácil que es hacer que el servicio funcione con XML o JSON.
Sin embargo te ha faltado mencionar un detalle que a mi me ha hecho perder un poco de tiempo hasta que he encontrado el problema, y es que no has comentando nada de que para usar las anotaciones @Xml* de la entidad Person es necesario usar la JDK 1.6. Yo suelo todavía usar por defecto la 1.5 y por eso me fallaba.
Salu2.
¡Muy buen post! Aquí te dejo un ejemplo relacionado con el tema, con una implementación de REST en Spring 3: Exponer un servicio web REST con Spring 3… Saludos 🙂
Enhorabuena, muy buen post. Una pregunta, el archivo pom al que te refieres, es un XML que se debe crear sólo con esa información o debe incluir algo más?
Muchas gracias
Hola jest, el pom.xml es el archivo de configuración de maven, en concreto para este módulo sería:
4.0.0
parent
com.autentia.tutoriales
1.0-SNAPSHOT
com.autentia.tutoriales
rest
1.0-SNAPSHOT
war
rest Maven Webapp
http://maven.apache.org
com.autentia.tutoriales
model
1.0-SNAPSHOT
org.springframework
spring-web
${spring.version}
org.springframework
spring-webmvc
${spring.version}
javax.servlet
servlet-api
2.5
provided
org.codehaus.jackson
jackson-mapper-asl
1.6.4
rest