Reputation: 42957
I am studying how Spring handle REST web services and I have some doubts related to the concept of the HttpMessageConverter.
On the official documentation I can read:
Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.
So the HttpMessageConverter seems to be an interface but what exactly is a strategy interface? Is something related to the strategy pattern or not?
So from what I have understand Spring automatically provide some implementations registered by default when using @EnableWebMvc or
But what exactly does these implementation? Can you provide me a practical example?
I think that it works in this way:
For example a client perform an HttpRequest putting in the body of this request a JSON message (I am not so practical but I think that I can do something like this), then the controller that handle this HttpRequst use an implementation of HttpMessageConverter to convert this JSON message into a model object. I think that it is true also the vice versa.
Is my reasoning correct or am I missing something?
Another doubt is related to the @RequestBody annotation (that I think it is related to the previous topic).
I have this example:
@RequestMapping(value="/orders/{id}", method=RequestMethod.PUT)
@ResponseStatus(HttpStatus.NO_CONTENT) // 204
public void updateOrder(@RequestBody Order updatedOrder, @PathVariable("id") long id) {
// process updated order data and return empty response
orderManager.updateOrder(id, updatedOrder);
}
So I think that @RequestBody Order updatedOrder take the value of the updatedOrder input parameter from the body of the HttpRequest and then convert it with into an Order object using an implementation of HttpMessageConverter.
Is it right or am I missing something? If it is right how can chose the right converter?
For example here I found another example similiar to the previous one: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html
@Controller
@RequestMapping(value = "/pets", method = RequestMethod.POST, consumes="application/json")
public void addPet(@RequestBody Pet pet, Model model) {
// implementation omitted
}
I think that here it is explicitly specified that it have to use a JSON to MODEL OBJECT converter. Why in the previous example is not specified? How can chose the right converter?
Tnx
Upvotes: 12
Views: 7448
Reputation: 279960
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
.
Upvotes: 20