How Does the Spring @Responsebody Annotation Work

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 HttpMessageConverters, 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



Leave a reply



Submit