¿Cómo funciona la anotación Spring @ResponseBody en este ejemplo de aplicación RESTful?

Tengo un método anotado de la siguiente manera:

/** * Provide a list of all accounts. */ // TODO 02: Complete this method. Add annotations to respond // to GET /accounts and return a List to be converted. // Save your work and restart the server. You should get JSON results when accessing // http://localhost:8080/rest-ws/app/accounts @RequestMapping(value="/orders", method=RequestMethod.GET) public @ResponseBody List accountSummary() { return accountManager.getAllAccounts(); } 

Entonces sé que con esta anotación:

 @RequestMapping(value="/orders", method=RequestMethod.GET) 

este método maneja las solicitudes GET HTTP hechas al recurso representado por la URL / órdenes .

Este método llama a un objeto DAO que devuelve una lista .

donde Account representa a un usuario en el sistema y tiene algunos campos que representan a este usuario, algo como:

 public class Account { @Id @Column(name = "ID") @GeneratedValue(strategy=GenerationType.IDENTITY) private Long entityId; @Column(name = "NUMBER") private String number; @Column(name = "NAME") private String name; @OneToMany(cascade=CascadeType.ALL) @JoinColumn(name = "ACCOUNT_ID") private Set beneficiaries = new HashSet(); ............................... ............................... ............................... } 

Mi pregunta es: ¿cómo funciona exactamente la anotación @ResponseBody ?

Está situado antes del objeto devuelto List así que creo que se refiere a esta Lista. La documentación del curso indica que esta anotación sirve a la función para:

asegúrese de que el resultado se escriba en la respuesta HTTP mediante un convertidor de mensajes HTTP (en lugar de una vista MVC).

Y también leyendo en la documentación oficial de Spring: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ResponseBody.html

parece que toma el objeto List y lo coloca en la Http Response . ¿Es correcto o estoy malentendiendo?

Escrito en el comentario del método anterior accountSummary() hay:

Deberías obtener los resultados de JSON al acceder a http: // localhost: 8080 / rest-ws / app / accounts

Entonces, ¿qué significa esto exactamente? ¿Significa que el objeto List devuelto por el método accountSummary() se convierte automáticamente a formato JSON y luego se coloca en la Http Response ? ¿O que?

Si esta afirmación es verdadera, ¿dónde se especifica que el objeto se convertirá automáticamente a formato JSON ? ¿Se adopta el formato estándar cuando se @ResponseBody anotación @ResponseBody o se especifica en otro lugar?

En primer lugar, la anotación no anota List . RequestMapping el método, tal como RequestMapping hace RequestMapping . Tu código es equivalente a

 @RequestMapping(value="/orders", method=RequestMethod.GET) @ResponseBody public List accountSummary() { return accountManager.getAllAccounts(); } 

Ahora lo que significa la anotación es que el valor devuelto del método constituirá el cuerpo de la respuesta HTTP. Por supuesto, una respuesta HTTP no puede contener objetos Java. Entonces, esta lista de cuentas se transforma a un formato adecuado para aplicaciones REST, generalmente JSON o XML.

La elección del formato depende de los convertidores de mensajes instalados, de los valores del atributo produces de la anotación RequestMapping y del tipo de contenido que acepta el cliente (que está disponible en los encabezados de solicitud HTTP). Por ejemplo, si la solicitud dice que acepta XML, pero no JSON, y hay un convertidor de mensajes instalado que puede transformar la lista a XML, se devolverá XML.

Lo primero que hay que entender es la diferencia en las architectures.

Un extremo tiene la architecture MVC, que se basa en su aplicación web normal, usa páginas web y el navegador solicita una página:

 Browser < ---> Controller < ---> Model | | +-View-+ 

El navegador realiza una solicitud, el controlador (@Controller) obtiene el modelo (@Entity) y crea la vista (JSP) del modelo y la vista se devuelve al cliente. Esta es la architecture básica de la aplicación web.

En el otro extremo, tienes una architecture RESTful. En este caso, no hay vista. El controlador solo envía de vuelta el modelo (o la representación de recursos, en términos más RESTful). El cliente puede ser una aplicación de JavaScript, una aplicación de servidor Java, cualquier aplicación en la que exponemos nuestra API REST. Con esta architecture, el cliente decide qué hacer con este modelo. Tomemos por ejemplo Twitter. API de Twitter como la Web (REST), que permite a nuestras aplicaciones utilizar su API para obtener actualizaciones de estado, de modo que podamos usarla para poner esa información en nuestra aplicación. Esa información vendrá en algún formato como JSON.

Dicho esto, cuando se trabaja con Spring MVC, primero se creó para manejar la architecture básica de la aplicación web. Hay diferentes sabores de firma de método que permiten que se produzca una vista a partir de nuestros métodos. El método podría devolver un ModelAndView donde lo creemos explícitamente, o hay formas implícitas en las que podemos devolver algún objeto arbitrario que se configure en los atributos del modelo. Pero de cualquier manera, en algún lugar a lo largo del ciclo solicitud-respuesta, habrá una vista producida.

Pero cuando usamos @ResponseBody , estamos diciendo que no queremos una vista producida. Solo queremos enviar el objeto de retorno como el cuerpo, en el formato que especifiquemos. No nos gustaría que sea un objeto Java serializado (aunque posible). Así que sí, necesita ser convertido a algún otro tipo común (este tipo normalmente se trata a través de la negociación de contenido – ver el enlace a continuación). Honestamente, no trabajo mucho con Spring, aunque me dedico aquí y allá. Normalmente, yo uso

 @RequestMapping(..., produces = MediaType.APPLICATION_JSON_VALUE) 

para establecer el tipo de contenido, pero tal vez JSON es el predeterminado. No me cites, pero si obtienes JSON y no has especificado el produces , entonces es posible que sea el predeterminado. JSON no es el único formato. Por ejemplo, lo anterior podría enviarse fácilmente en XML, pero necesitaría tener produces en MediaType.APPLICATION_XML_VALUE y creo que necesita configurar HttpMessageConverter para JAXB. En cuanto al JSON MappingJacksonHttpMessageConverter configurado, cuando tenemos a Jackson en el classpath.

Me tomaría un tiempo para aprender sobre la negociación de contenido . Es una parte muy importante de REST. Te ayudará a aprender sobre los diferentes formatos de respuesta y cómo asignarlos a tus métodos.

Como lo menciona JB Nizet,

 @RequestMapping(value="/orders", method=RequestMethod.GET) @ResponseBody public List accountSummary() { return accountManager.getAllAccounts(); } 

y

 @RequestMapping(value="/orders", method=RequestMethod.GET) public @ResponseBody List accountSummary() { return accountManager.getAllAccounts(); } 

ambos son lo mismo. como @ResponseBody anota el método, no la lista. @GMsoF: los convertidores de mensajes instalados aquí se pueden usar de la siguiente manera.

 @RequestMapping(value="/orders", method=RequestMethod.GET , produces={"application/json","application/xml"}) @ResponseBody public List accountSummary() { return accountManager.getAllAccounts(); } 

Gracias 🙂

Además de esto, el tipo de retorno está determinado por

  1. Lo que la Solicitud HTTP dice que quiere: en su encabezado Aceptar. Intente ver la solicitud inicial como ve en qué se establece Aceptar.

  2. Qué establece HttpMessageConverters Spring. Spring MVC configurará convertidores para XML (usando JAXB) y JSON si las bibliotecas de Jackson están en classpath.

Si hay una opción, elige una: en este ejemplo, pasa a ser JSON.

Esto está cubierto en las notas del curso. Busque las notas sobre Convertidores de mensajes y Negociación de contenido.