How does the Spring @ResponseBody annotation work?
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.
@RequestBody and @ResponseBody annotations in Spring
There is a whole Section in the docs called 16.3.3.4 Mapping the request body with the @RequestBody annotation. And one called 16.3.3.5 Mapping the response body with the @ResponseBody annotation. I suggest you consult those sections. Also relevant: @RequestBody
javadocs, @ResponseBody
javadocs
Usage examples would be something like this:
Using a JavaScript-library like JQuery, you would post a JSON-Object like this:
{ "firstName" : "Elmer", "lastName" : "Fudd" }
Your controller method would look like this:
// controller
@ResponseBody @RequestMapping("/description")
public Description getDescription(@RequestBody UserStats stats){
return new Description(stats.getFirstName() + " " + stats.getLastname() + " hates wacky wabbits");
}
// domain / value objects
public class UserStats{
private String firstName;
private String lastName;
// + getters, setters
}
public class Description{
private String description;
// + getters, setters, constructor
}
Now if you have Jackson on your classpath (and have an <mvc:annotation-driven>
setup), Spring would convert the incoming JSON to a UserStats object from the post body (because you added the @RequestBody
annotation) and it would serialize the returned object to JSON (because you added the @ResponseBody
annotation). So the Browser / Client would see this JSON result:
{ "description" : "Elmer Fudd hates wacky wabbits" }
See this previous answer of mine for a complete working example: https://stackoverflow.com/a/5908632/342852
Note: RequestBody / ResponseBody is of course not limited to JSON, both can handle multiple formats, including plain text and XML, but JSON is probably the most used format.
Update
Ever since Spring 4.x, you usually won't use @ResponseBody
on method level, but rather @RestController
on class level, with the same effect.
Here is a quote from the official Spring MVC documentation:
@RestController
is a composed annotation that is itself meta-annotated
with@Controller
and@ResponseBody
to indicate a controller whose
every method inherits the type-level@ResponseBody
annotation and,
therefore, writes directly to the response body versus view resolution
and rendering with an HTML template.
Return only string message from Spring MVC 3 Controller
Annotate your method in controller with @ResponseBody
:
@RequestMapping(value="/controller", method=GET)
@ResponseBody
public String foo() {
return "Response!";
}
From: 15.3.2.6 Mapping the response body with the @ResponseBody
annotation:
The
@ResponseBody
annotation [...] can be put on a method and indicates that the return type should be written straight to the HTTP response body (and not placed in a Model, or interpreted as a view name).
Using ResponseBody annotation in Spring for returning a Json not working
So the problem was that i didn'd have all the Jackson dependencies declared in pom.xml.
These are the dependencies for your maven project in case you want Spring 3 to automatically serialize an object for you , using the @ResponseBody annotation , as a response from a method.
Noob stuff , but I didn't saw this mentioned in the examples that I found .
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.9</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.9</version>
</dependency>
Also , I had to change some stuff in the ajax call for invoking the method that is returning the json object with data for the chart generation :
$("#buttonPieGenerate").live("click", function(){
$.ajax({
url: "drawPieChart", //method from controller
contentType: "application/json",
data: params,
success: function(data) {
drawPieChart(data.percentages, data.topics,'chart_div',600,400);
}
});
});
I'm accessing the data in the Json object that I`m getting as a response from the call with data.percentages , data.topics .
How exactly works @RequestBody annotation and how it is related to the HttpMessageConverter interface?
Handler method parameters are generated by Spring's HandlerMethodArgumentResolver
and handler method return values are processed by Spring's HandlerMethodReturnValueHandler
. The implementation that deals with both @ResponseBody
and @RequestBody
is RequestResponseBodyMethodProcessor
.
One of these is registered by default (@EnableWebMvc
configuration) with a default list of HttpMessageConverter
instances. This is done in WebMvcConfigurationSupport#addDefaultHttpMessageConverters(List)
. You can find the source code and see which ones are added and in what order.
When Spring goes to generate an argument for a @RequestBody
parameter, it loops through the HttpMessageConverter
instances, checks if that instance HttpMessageConverter#canRead
the content type given in the request and can generate an instance of the parameter type. If it can, Spring will use that HttpMessageConverter
to produce an argument. If it can't, Spring will skip it and try the next instance, until it runs out. At which point, it will throw an exception.
For @ResponseBody
, the process is the same except Spring now uses HttpMessageConverter#canWrite
. It will check if the HttpMessageConverter
can serialize the return type and generate response content that fits the content type that is expected in the response (given in the Accept
request header).
The consumes
attribute of @RequestParam
@RequestMapping(value = "/pets", method = RequestMethod.POST, consumes="application/json")
has nothing to do with the strategy declared above. The only thing that consumes
does here is to restrict the mapping of the handler. For example, take these two handlers
@RequestMapping(value = "/pets", method = RequestMethod.POST)
@RequestMapping(value = "/pets", method = RequestMethod.POST, consumes="application/json")
The first one can handle any request to /pets
with any content type. The second can only handle those requests to /pets
with the content type application/json
.
Difference between spring @Controller and @RestController annotation
@Controller
is used to mark classes as Spring MVC Controller.@RestController
is a convenience annotation that does nothing more than adding the@Controller
and@ResponseBody
annotations (see: Javadoc)
So the following two controller definitions should do the same
@Controller
@ResponseBody
public class MyController { }
@RestController
public class MyRestController { }
What is difference between @RequestBody and @RequestParam?
@RequestParam
annotated parameters get linked to specific Servlet request parameters. Parameter values are converted to the declared method argument type.
This annotation indicates that a method parameter should be bound to a web request parameter.
For example Angular request for Spring RequestParam(s) would look like that:
$http.post('http://localhost:7777/scan/l/register?username="Johny"&password="123123"&auth=true')
.success(function (data, status, headers, config) {
...
})
Endpoint with RequestParam:
@RequestMapping(method = RequestMethod.POST, value = "/register")
public Map<String, String> register(Model uiModel,
@RequestParam String username,
@RequestParam String password,
@RequestParam boolean auth,
HttpServletRequest httpServletRequest) {...
@RequestBody
annotated parameters get linked to the HTTP request body. Parameter values are converted to the declared method argument type using HttpMessageConverters.
This annotation indicates a method parameter should be bound to the body of the web request.
For example Angular request for Spring RequestBody would look like that:
$scope.user = {
username: "foo",
auth: true,
password: "bar"
};
$http.post('http://localhost:7777/scan/l/register', $scope.user).
success(function (data, status, headers, config) {
...
})
Endpoint with RequestBody:
@RequestMapping(method = RequestMethod.POST, produces = "application/json",
value = "/register")
public Map<String, String> register(Model uiModel,
@RequestBody User user,
HttpServletRequest httpServletRequest) {...
Spring : Why should I still use @RequestBody when my class is already annotated with @RestController?
@RestController
contains @ResponseBody
so you do not need this any more.
But you still need the @RequestBody
annotation, because the method you call for a POST request might contain more than one parameters, one of which is mapped to the request body, other parameters of the method might be PathVariables
or for example a UriComponentsBuilder
.
And to mark the parameter which is to be mapped to the request body, you need the annotation.
Who sets response content-type in Spring MVC (@ResponseBody)
Simple declaration of the StringHttpMessageConverter
bean is not enough, you need to inject it into AnnotationMethodHandlerAdapter
:
<bean class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<array>
<bean class = "org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value = "text/plain;charset=UTF-8" />
</bean>
</array>
</property>
</bean>
However, using this method you have to redefine all HttpMessageConverter
s, and also it doesn't work with <mvc:annotation-driven />
.
So, perhaps the most convenient but ugly method is to intercept instantiation of the AnnotationMethodHandlerAdapter
with BeanPostProcessor
:
public class EncodingPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String name)
throws BeansException {
if (bean instanceof AnnotationMethodHandlerAdapter) {
HttpMessageConverter<?>[] convs = ((AnnotationMethodHandlerAdapter) bean).getMessageConverters();
for (HttpMessageConverter<?> conv: convs) {
if (conv instanceof StringHttpMessageConverter) {
((StringHttpMessageConverter) conv).setSupportedMediaTypes(
Arrays.asList(new MediaType("text", "html",
Charset.forName("UTF-8"))));
}
}
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String name)
throws BeansException {
return bean;
}
}
-
<bean class = "EncodingPostProcessor " />
Related Topics
Java: Object to Byte[] and Byte[] to Object Converter (For Tokyo Cabinet)
Comparing Strings by Their Alphabetical Order
How to Serve Jsps from Inside a Jar in Lib, or Is There a Workaround
Creating Classes Dynamically with Java
How to Load/Reference a File as a File Instance from the Classpath
Difference Between Double and Double in Comparison
Spring MVC Type Conversion:Propertyeditor or Converter
Cannot Find Main Class in File Compiled with Ant
How to Get the Current Date and Time of Your Timezone in Java
Could Not Find Method Compile() for Arguments Gradle
How to Get Current Working Directory in Java
Spring Configure @Responsebody JSON Format
Efficiently Compute Intersection of Two Sets in Java
How to Create a Class Literal of a Known Type: Class<List<String>>
How to Get the Real Path of Java Application at Runtime