¿Cómo funciona la anotación Spring @ResponseBody?

89

Tengo un método que está 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<Account> 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<Account> accountSummary() {
    return accountManager.getAllAccounts();
}

Entonces sé que por esta anotación:

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

este método maneja las solicitudes GET HTTP realizadas al recurso representado por la URL / pedidos .

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

donde Cuenta 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<Beneficiary> beneficiaries = new HashSet<Beneficiary>();

    ...............................
    ...............................
    ...............................
}

Mi pregunta es: ¿Cómo funciona exactamente la @ResponseBodyanotación?

Está situado antes del List<Account>objeto devuelto, así que creo que se refiere a esta Lista. La documentación del curso establece que esta anotación tiene la función de:

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 sobre 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 List<Account>objeto y lo coloca en el Http Response. ¿Es esto correcto o lo estoy entendiendo mal?

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

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

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

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

AndreaNobili
fuente

Respuestas:

160

En primer lugar, la anotación no incluye anotaciones List. Anota el método, al igual que lo RequestMappinghace. Tu código es equivalente a

@RequestMapping(value="/orders", method=RequestMethod.GET)
@ResponseBody
public List<Account> 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 producesatributo de la @RequestMappinganotación 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, entonces se devolverá XML.

JB Nizet
fuente
3
hola, ¿cómo configuramos los "convertidores de mensajes instalados"? Quiero que el valor predeterminado siempre se convierta a Json. ¿Puedo hacer eso?
Sam YC
@JB Nizet, ¿puede explicar por qué la respuesta http no puede contener objetos java? Soy un novato en Java.
Naved Ali
3
@ Er.NavedAli leyó la especificación http, el tipo de contenido de respuesta http define el contenido legal que puede contener una respuesta http. Los valores legales para eso pueden ser "application / octet-stream", "image / jpeg", "text / HTML", pero los objetos java no son un valor legal para ellos.
ZhaoGang
@ZhaoGang, el tipo de contenido 'application / json' ha sido reemplazado por 'application / xml' en mi caso de prueba, pero el cuerpo de la respuesta parece no haber cambiado, todavía está presente el formato json.
LeafiWan
1
¿Dónde exactamente, quiero decir, en qué clase de primavera, se toma la decisión para determinar cómo devolver la respuesta? ¿Puede indicarme la fuente en github, dónde se toma esta decisión o el nombre de la clase? Además, ¿qué pasará si el cliente acepta XML, pero no se instala ningún convertidor XML?
anir
66

Lo primero que hay que entender es la diferencia de arquitecturas.

En un extremo, tiene la arquitectura MVC, que se basa en su aplicación web normal, utiliza 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) a partir del modelo y la vista se devuelve al cliente. Esta es la arquitectura básica de la aplicación web.

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

Dicho esto, al trabajar con Spring MVC, primero se creó para manejar la arquitectura básica de la aplicación web. Existen diferentes tipos de firmas de métodos que permiten producir una vista a partir de nuestros métodos. El método podría devolver un ModelAndViewdonde lo creamos explícitamente, o hay formas implícitas en las que podemos devolver algún objeto arbitrario que se establece en los atributos del modelo. Pero de cualquier manera, en algún momento del ciclo de solicitud-respuesta, se producirá una vista.

Pero cuando usamos @ResponseBody, estamos diciendo que no queremos que se produzca una vista. Solo queremos enviar el objeto de retorno como cuerpo, en cualquier formato que especifiquemos. No quisiéramos que fuera un objeto Java serializado (aunque es posible). Entonces, sí, debe convertirse a algún otro tipo común (este tipo normalmente se trata a través de la negociación de contenido; consulte el enlace a continuación). Honestamente, no trabajo mucho con Spring, aunque me dedico a ello aquí y allá. Normalmente, uso

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

para establecer el tipo de contenido, pero tal vez JSON sea el predeterminado. No me cite, pero si obtiene JSON y no ha especificado el produces, entonces tal vez sea el predeterminado. JSON no es el único formato. Por ejemplo, lo anterior podría enviarse fácilmente en XML, pero necesitaría tener el producesto MediaType.APPLICATION_XML_VALUEy creo que debe configurarlo HttpMessageConverterpara JAXB. En cuanto al JSON MappingJacksonHttpMessageConverterconfigurado, 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. Le ayudará a aprender sobre los diferentes formatos de respuesta y cómo asignarlos a sus métodos.

Paul Samsotha
fuente
Si encontró su respuesta mientras investigaba cómo crear un controlador REST genérico / dinámico, sin usar @Controller/@RestController. Descubrí que necesito omitir de alguna manera la capa de resolución de vistas. No es tan simple porque la clase AbstractController proporciona un método que debe devolver el nombre de la vista. Hice una pregunta al respecto: stackoverflow.com/questions/41016018/… , si tiene algunas ideas sobre cómo puedo resolver mi problema, publique un comentario.
nowszy94
1

Además de esto, el tipo de devolución está determinado por

  1. Lo que la solicitud HTTP dice que quiere: en su encabezado Accept. Intente mirar la solicitud inicial para ver en qué está configurado Aceptar.

  2. Lo que configura HttpMessageConverters Spring. Spring MVC configurará convertidores para XML (usando JAXB) y JSON si las bibliotecas de Jackson están en la ruta de clases.

Si hay una opción, elige una; en este ejemplo, resulta ser JSON.

Esto se trata en las notas del curso. Busque las notas sobre convertidores de mensajes y negociación de contenido.

Paulchapman
fuente