@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, andServletModelAttributeMethodProcessor
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
How to Avoid Floating Point Precision Errors with Floats or Doubles in Java
How to Invoke a Linux Shell Command from Java
The Performance Impact of Using Instanceof in Java
What Is the Java's Internal Represention for String? Modified Utf-8? Utf-16
Calculate Size of Object in Java
How Come Invoking a (Static) Method on a Null Reference Doesn't Throw Nullpointerexception
In Java, How to Parse Xml as a String Instead of a File
How to Make a Jframe Modal in Swing Java
How to Bundle a Native Library and a Jni Library Inside a Jar
What's the C++ Idiom Equivalent to the Java Static Block
Java List.Add() Unsupportedoperationexception
Simpledateformat and Locale Based Format String
How to Get Parameters from the Url with Jsp