Como funciona a anotação Spring @ResponseBody?

89

Eu tenho um método que é anotado da seguinte maneira:

/**
* 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();
}

Então eu sei que por esta anotação:

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

este método manipula solicitações GET HTTP feitas ao recurso representado pela URL / pedidos .

Este método chama um objeto DAO que retorna uma Lista .

onde Conta representa um usuário no sistema e tem alguns campos que representam esse usuário, 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>();

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

Minha pergunta é: como exatamente funciona a @ResponseBodyanotação?

Ele está situado antes do List<Account>objeto retornado, então acho que se refere a esta Lista. A documentação do curso afirma que esta anotação tem a função de:

certifique-se de que o resultado será gravado na resposta HTTP por um conversor de mensagem HTTP (em vez de uma visualização MVC).

E também lendo sobre a documentação oficial do Spring: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ResponseBody.html

parece que pega o List<Account>objeto e o coloca no Http Response. Isso é correto ou estou entendendo mal?

Escrito no comentário do accountSummary()método anterior , há:

Você deve obter resultados JSON ao acessar http: // localhost: 8080 / rest-ws / app / accounts

Então, o que exatamente isso significa? Isso significa que o List<Account>objeto retornado pelo accountSummary()método é automaticamente convertido para o JSONformato e, em seguida, colocado no Http Response? Ou o que?

Se esta afirmação for verdadeira, onde é especificado que o objeto será convertido automaticamente para o JSONformato? O formato padrão é adotado quando a @ResponseBodyanotação é usada ou é especificado em outro lugar?

AndreaNobili
fonte

Respostas:

160

Em primeiro lugar, a anotação não faz anotações List. Ele anota o método, assim como RequestMappingfaz. Seu código é equivalente a

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

Agora, o que a anotação significa é que o valor retornado do método constituirá o corpo da resposta HTTP. Obviamente, uma resposta HTTP não pode conter objetos Java. Portanto, essa lista de contas é transformada em um formato adequado para aplicativos REST, normalmente JSON ou XML.

A escolha do formato depende dos conversores de mensagens instalados, dos valores do producesatributo da @RequestMappinganotação e do tipo de conteúdo que o cliente aceita (que está disponível nos cabeçalhos de solicitação HTTP). Por exemplo, se a solicitação disser que aceita XML, mas não JSON, e houver um conversor de mensagem instalado que pode transformar a lista em XML, o XML será retornado.

JB Nizet
fonte
3
oi, como configuramos os "conversores de mensagem instalados"? Eu quero que o padrão seja sempre convertido para Json. Eu sou capaz de fazer isso?
GMsoF
@JB Nizet pode explicar porque a resposta http não pode conter objetos java. Eu sou um newbee para java.
Naved Ali em
3
@ Er.NavedAli leia a especificação http, o tipo de conteúdo de resposta http define o conteúdo legal que uma resposta http pode conter. os valores legais para isso podem ser "application / octet-stream", "image / jpeg", "text / HTML", mas os objetos java não são um valor legal para isso.
ZhaoGang
@ZhaoGang, Content-type 'application / json' foi substituído por 'application / xml' no meu caso de teste, mas o corpo da resposta parece não ter mudado, ainda apresenta o formato json.
LeafiWan
1
onde exatamente, quero dizer, em qual aula de primavera, é tomada a decisão de determinar como retornar a resposta? Você pode me apontar a fonte no github, onde esta decisão é feita ou o nome da classe? Além disso, o que acontecerá se o cliente aceitar XML, mas nenhum conversor XML estiver instalado?
anir 01 de
63

A primeira coisa básica a entender é a diferença nas arquiteturas.

De um lado, você tem a arquitetura MVC, que é baseada em seu aplicativo da web normal, usando páginas da web, e o navegador faz uma solicitação de uma página:

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

O navegador faz uma solicitação, o controlador (@Controller) obtém o modelo (@Entity) e cria a visualização (JSP) a partir do modelo e a visualização é retornada ao cliente. Esta é a arquitetura básica do aplicativo da web.

Por outro lado, você tem uma arquitetura RESTful. Nesse caso, não há Visualização. O Controlador apenas envia de volta o modelo (ou representação de recurso, em termos mais RESTful). O cliente pode ser um aplicativo JavaScript, um aplicativo de servidor Java, qualquer aplicativo ao qual expomos nossa API REST. Com essa arquitetura, o cliente decide o que fazer com este modelo. Tomemos, por exemplo, o Twitter. Twitter como a API da Web (REST), que permite que nossos aplicativos usem sua API para obter coisas como atualizações de status, para que possamos usá-la para colocar esses dados em nosso aplicativo. Esses dados virão em algum formato como JSON.

Dito isso, ao trabalhar com Spring MVC, ele foi construído primeiro para lidar com a arquitetura básica do aplicativo da web. Existem muitos sabores de assinatura de método diferentes que permitem que uma visualização seja produzida a partir de nossos métodos. O método pode retornar um ModelAndViewonde nós o criamos explicitamente, ou existem maneiras implícitas onde podemos retornar algum objeto arbitrário que é definido nos atributos do modelo. Mas de qualquer maneira, em algum lugar ao longo do ciclo de solicitação-resposta, haverá uma visualização produzida.

Mas quando usamos @ResponseBody, estamos dizendo que não queremos uma visualização produzida. Queremos apenas enviar o objeto de retorno como o corpo, em qualquer formato que especificarmos. Não queremos que seja um objeto Java serializado (embora possível). Então, sim, ele precisa ser convertido para algum outro tipo comum (esse tipo normalmente é tratado por meio da negociação de conteúdo - veja o link abaixo). Honestamente, não trabalho muito com o Spring, embora me divirta com ele aqui e ali. Normalmente, eu uso

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

para definir o tipo de conteúdo, mas talvez JSON seja o padrão. Não me cite, mas se você está recebendo JSON e não especificou o produces, então talvez seja o padrão. JSON não é o único formato. Por exemplo, o texto acima pode ser facilmente enviado em XML, mas você precisa ter o producespara MediaType.APPLICATION_XML_VALUEe eu acredito que você precisa configurar o HttpMessageConverterpara JAXB. Quanto ao JSON MappingJacksonHttpMessageConverterconfigurado, quando temos Jackson no classpath.

Eu levaria algum tempo para aprender sobre negociação de conteúdo . É uma parte muito importante do REST. Isso o ajudará a aprender sobre os diferentes formatos de resposta e como mapeá-los para seus métodos.

Paul Samsotha
fonte
Se encontrou sua resposta durante a investigação sobre como criar um controlador REST genérico / dinâmico, sem usar @Controller/@RestController. Eu descobri que preciso omitir de alguma forma a camada de resolução de visualizações Não é tão simples porque a classe AbstractController fornece um método que deve retornar o nome da visão. Eu fiz uma pergunta sobre isso: stackoverflow.com/questions/41016018/… , se você tiver algumas idéias sobre como posso resolver meu problema, por favor poste um comentário.
nowszy94
1

Além disso, o tipo de retorno é determinado por

  1. O que a solicitação HTTP diz que deseja - em seu cabeçalho Aceitar. Tente examinar a solicitação inicial para ver como Aceitar está definido.

  2. O que HttpMessageConverters Spring configura. Spring MVC irá configurar conversores para XML (usando JAXB) e JSON se as bibliotecas Jackson estiverem no classpath.

Se houver uma opção, ele escolhe uma - neste exemplo, é JSON.

Isso é abordado nas notas do curso. Procure as notas sobre Conversores de mensagens e negociação de conteúdo.

Paulchapman
fonte