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?
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.
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.
As mentioned by JB Nizet,
@RequestMapping(value="/orders", method=RequestMethod.GET)
@ResponseBody
public List<Account> accountSummary() {
return accountManager.getAllAccounts();
}
and
@RequestMapping(value="/orders", method=RequestMethod.GET)
public @ResponseBody List<Account> accountSummary() {
return accountManager.getAllAccounts();
}
both are same. as @ResponseBody annotates the method not the list.
@GMsoF -Installed message converters here can be used as follows.
@RequestMapping(value="/orders", method=RequestMethod.GET , produces={"application/json","application/xml"})
@ResponseBody
public List<Account> accountSummary() {
return accountManager.getAllAccounts();
}
Thanks :)
Further to this, the return type is determined by
What the HTTP Request says it wants - in its Accept header. Try looking at the initial request as see what Accept is set to.
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.