@Requestbody and @Responsebody Annotations in Spring

@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.

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.

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) {...

Hope this helps.

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.

Is it necessary to serialize objects for using @RequestBody @ResponseBody annotations

It does not. When you return an instance from controller method annotated with @ResponseBody you might say that it is serialized to JSON for instance. But this kind of serialization is not the Java serialization which involves Serializable interface.

@RequestBody and @ResponseBody annotations are handled by RequestResponseBodyMethodProcessor which uses HttpMessageConverter implementations to perform the conversion for example from object to JSON or from JSON to object.

When you look at the HttpMessageConverter interface there is a canRead method which has the following signature: boolean canRead(Class<?> clazz, MediaType mediaType); and as you can see it is not bound to only serializable classes using generics.

Sources and more information

  • RequestResponseBodyMethodProcessor JavaDoc
  • HttpMessageConverter JavaDoc

Spring @RequestBody annotation in Restful web service

No, you'll have to specify @RequestBody. A Java method can have only a single return value, and so the @ResponseBody is unambiguous, but there are multiple possible ways that mapped controller parameters might be interpreted (in particular, using @ModelAttribute with form encoding is a very common alternative to @RequestBody with JSON), and you'll need to tell Spring how to map the incoming request.

Combine functionality of @ModelAttribute and @RequestBody in Spring MVC

Turns out no, not at time of writing anyway.

RequestMappingHandlerAdapter.getDefaultArgumentResolvers holds the default configuration for the argument resovlers, and we have:

  • RequestResponseBodyMethodProcessor handling the @RequestBody annotation, and
  • ServletModelAttributeMethodProcessor handling the @ModelAttribute, or any parameter without any annotations.

It's always just one single resolver that gets executed, even if you try using a composite.

My solution

@ControllerAdvice
@RequiredArgsConstructor
@Order(HIGHEST_PRECEDENCE)
public class ServletRequestBinderRequestBodyAdvice extends RequestBodyAdviceAdapter {

private final ServletRequest servletRequest;

@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}

@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
copyDefaultPropertiesThatWhereOverwritenWithNull(parameter, body);

new ExtendedServletRequestDataBinder(body).bind(servletRequest);

return body;
}

private void copyDefaultPropertiesThatWhereOverwritenWithNull(MethodParameter parameter, Object arg) {
Object argWithDefaults = instantiateClass(parameter.getParameterType());
copyPropertiesSkippingNulls(argWithDefaults, arg);
}

}

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.



Related Topics



Leave a reply



Submit