How does the Spring @ResponseBody annotation work?
Asked Answered
J

5

106

I have a method that is annotated in the following way:

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

So I know that by this annotation:

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

this method handle GET HTTP requests made to the resource represented by the URL /orders.

This method calls a DAO object that returns a List.

where Account represents a user on the system and has some fields that represent this user, something like:

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>();

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

My question is: How exactly the does the @ResponseBody annotation work?

It is situated before the returned List<Account> object so I think that it refers to this List. The course documentation states that this annotation serves the function to:

ensure that the result will be written to the HTTP response by an HTTP Message Converter (instead of an MVC View).

And also reading on the official Spring documentation: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ResponseBody.html

it seems that it takes the List<Account> object and puts it into the Http Response. Is this correct or am I misunderstanding?

Written into the comment of the previous accountSummary() method there is:

You should get JSON results when accessing http://localhost:8080/rest-ws/app/accounts

So what exactly does this mean? Does it mean that the List<Account> object returned by the accountSummary() method is automatically converted into JSON format and then put into the Http Response? Or what?

If this assertion is true, where is it specified that the object will be automatically converted into JSON format? Is the standard format adopted when the @ResponseBody annotation is used or is it specified elsewhere?

Jourdain answered 21/2, 2015 at 13:28 Comment(0)
P
184

First of all, the annotation doesn't annotate List. It annotates the method, just as RequestMapping does. Your code is equivalent to

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

Now what the annotation means is that the returned value of the method will constitute the body of the HTTP response. Of course, an HTTP response can't contain Java objects. So this list of accounts is transformed to a format suitable for REST applications, typically JSON or XML.

The choice of the format depends on the installed message converters, on the values of the produces attribute of the @RequestMapping annotation, and on the content type that the client accepts (that is available in the HTTP request headers). For example, if the request says it accepts XML, but not JSON, and there is a message converter installed that can transform the list to XML, then XML will be returned.

Patency answered 21/2, 2015 at 14:53 Comment(7)
hi, how do we setup the "installed message converters"? I want the default always convert to Json. Am I able to do that?Debrahdebrecen
@JB Nizet can you please explain why http response cannot contain java objects. I am a newbee to java.Alfrediaalfredo
@Er.NavedAli read the http specification, the http response content type defines the legal content that a http response can contain. legal values for that can be "application/octet-stream", "image/jpeg", "text/HTML", but java objects is not a legal value for it.Griswold
@ZhaoGang, Content-type 'application/json' has been replaced by 'application/xml' in my test case, but the response body seems no change, still present json format.Pugging
where exactly, I mean in which spring class, the decision to determine how to return response is made? Can you point me to source on github, where this decision is done or class name? Also what will happen if client accepts XML, but no XML converter is installed?Castera
What's not clear to me about the @ResponseBody annotation is how I would set the HttpStatus of the returned result. Is there a way to do this?Scanties
Interesting. The returned object is in the body of my response anyway (JS's fetch() returns a Promise which is then resolved into a Response which I can then call json() on, asynchronously), even though I don't have any @ResponseBody annotations. I wonder why I may need itHartfield
P
85

The first basic thing to understand is the difference in architectures.

One end you have the MVC architecture, which is based on your normal web app, using web pages, and the browser makes a request for a page:

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

The browser makes a request, the controller (@Controller) gets the model (@Entity), and creates the view (JSP) from the model and the view is returned back to the client. This is the basic web app architecture.

On the other end, you have a RESTful architecture. In this case, there is no View. The Controller only sends back the model (or resource representation, in more RESTful terms). The client can be a JavaScript application, a Java server application, any application in which we expose our REST API to. With this architecture, the client decides what to do with this model. Take for instance Twitter. Twitter as the Web (REST) API, that allows our applications to use its API to get such things as status updates, so that we can use it to put that data in our application. That data will come in some format like JSON.

That being said, when working with Spring MVC, it was first built to handle the basic web application architecture. There are may different method signature flavors that allow a view to be produced from our methods. The method could return a ModelAndView where we explicitly create it, or there are implicit ways where we can return some arbitrary object that gets set into model attributes. But either way, somewhere along the request-response cycle, there will be a view produced.

But when we use @ResponseBody, we are saying that we do not want a view produced. We just want to send the return object as the body, in whatever format we specify. We wouldn't want it to be a serialized Java object (though possible). So yes, it needs to be converted to some other common type (this type is normally dealt with through content negotiation - see link below). Honestly, I don't work much with Spring, though I dabble with it here and there. Normally, I use

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

to set the content type, but maybe JSON is the default. Don't quote me, but if you are getting JSON, and you haven't specified the produces, then maybe it is the default. JSON is not the only format. For instance, the above could easily be sent in XML, but you would need to have the produces to MediaType.APPLICATION_XML_VALUE and I believe you need to configure the HttpMessageConverter for JAXB. As for the JSON MappingJacksonHttpMessageConverter configured, when we have Jackson on the classpath.

I would take some time to learn about Content Negotiation. It's a very important part of REST. It'll help you learn about the different response formats and how to map them to your methods.

Paluas answered 21/2, 2015 at 14:49 Comment(1)
If found your answer while investigation on how to create a generic/dynamic REST controller, without using @Controller/@RestController. I discovered, that I need to omit somehow the view resolver layer. It's not so simple because AbstractController class provides a method that must return view name. I asked a question about that: #41016518, if you have some ideas on how can I solve my problem, please post a comment.Wilber
R
2

Further to this, the return type is determined by

  1. What the HTTP Request says it wants - in its Accept header. Try looking at the initial request as see what Accept is set to.

  2. What HttpMessageConverters Spring sets up. Spring MVC will setup converters for XML (using JAXB) and JSON if Jackson libraries are on he classpath.

If there is a choice it picks one - in this example, it happens to be JSON.

This is covered in the course notes. Look for the notes on Message Convertors and Content Negotiation.

Rosen answered 8/7, 2016 at 4:34 Comment(0)
T
1

@RequestBody annotation binds the HTTPRequest body to the domain object. Spring automatically deserializes incoming HTTP Request to object using HttpMessageConverters. HttpMessageConverter converts body of request to resolve the method argument depending on the content type of the request. Many examples how to use converters https://upcodein.com/search/jc/mg/ResponseBody/page/0

Trilateral answered 23/2, 2021 at 21:34 Comment(0)
B
0

I would like to include additional information about:

How can message converters be configured in terms of response body? To handle the serialization of responses using the HttpMessageCoverter interface. Spring Boot provides several built-in message converters for data formats such as JSON and XML. You can also create custom message converters to handle other data formats.

Briannebriano answered 14/4, 2023 at 22:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.