原文:http://www.baeldung.com/spring-httpmessageconverter-restjavascript
This article describes how to Configure HttpMessageConverter in Spring.java
Simply put, message converters are used to marshall and unmarshall Java Objects to and from JSON, XML, etc – over HTTP.web
2.1. Enable Web MVCspring
The Web Application needs to be configured with Spring MVC support – one convenient and very customizable way to do this is to use the @EnableWebMvc annotation:shell
1
2
3
4
5
6
|
@EnableWebMvc
@Configuration
@ComponentScan
({
"org.baeldung.web"
})
public
class
WebConfig
extends
WebMvcConfigurerAdapter {
...
}
|
Note that this class extends WebMvcConfigurerAdapter – which will allow us to change the default list of Http Converters with our own.json
By default, the following HttpMessageConverters instances are pre-enabled:api
Each HttpMessageConverter implementation has one or several associated MIME Types.bash
When receiving a new request, Spring will use of the 「Accept」 header to determine the media type that it needs to respond with.app
It will then try to find a registered converter that is capable of handling that specific media type – and it will use it to convert the entity and send back the response.curl
The process is similar for receiving a request which contains JSON information – the framework will use the 「Content-Type」 header to determine the media type of the request body.
It will then search for a HttpMessageConverter that can convert the body sent by the client to a Java Object.
Let’s clarify this with a quick example:
Let’s now look at the specifics of how this works – and how we should leverage the@ResponseBody and @RequestBody annotations.
@ResponseBody on a Controller method indicates to Spring that the return value of the method is serialized directly to the body of the HTTP Response. As discussed above, the 「Accept」 header specified by the Client will be used to choose the appropriate Http Converter to marshall the entity.
Let’s look at a simple example:
1
2
3
4
|
@RequestMapping
(method=RequestMethod.GET, value=
"/foos/{id}"
)
public
@ResponseBody
Foo findById(
@PathVariable
long
id) {
return
fooService.get(id);
}
|
Now, the client will specify the 「Accept」 header to application/json in the request – example curl command:
curl --header
"Accept: application/json"
http:
//localhost
:8080
/spring-rest/foos/1
|
The Foo class:
1
2
3
4
|
public
class
Foo {
private
long
id;
private
String name;
}
|
And the Http Response Body:
1
2
3
4
|
{
"id"
: 1,
"name"
:
"Paul"
,
}
|
@RequestBodyis used on the argument of a Controller method – it indicates to Spring that the body of the HTTP Request is deserialized to that particular Java entity. As discussed previously, the 「Content-Type」 header specified by the Client will be used to determine the appropriate converter for this.
Let’s look at an example:
1
2
3
4
|
@RequestMapping
(method=RequestMethod.PUT, value=
"/foos/{id}"
)
public
@ResponseBody
void
updateFoo(
@RequestBody
Foo foo,
@PathVariable
String id) {
fooService.update(foo);
}
|
Now, let’s consume this with a JSON object – we’re specifying 「Content-Type」 to beapplication/json:
curl -i -X PUT -H
"Content-Type: application/json"
-d
'{"id":"83","name":"klik"}'
http:
//localhost
:8080
/spring-rest/foos/1
|
We get back a 200 OK – a successful response:
1
2
3
4
|
HTTP
/1
.1 200 OK
Server: Apache-Coyote
/1
.1
Content-Length: 0
Date: Fri, 10 Jan 2014 11:18:54 GMT
|
We can customize the message converters by extending the WebMvcConfigurerAdapter class and overriding the configureMessageConvertersmethod:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@EnableWebMvc
@Configuration
@ComponentScan
({
"org.baeldung.web"
})
public
class
WebConfig
extends
WebMvcConfigurerAdapter {
@Override
public
void
configureMessageConverters(List<HttpMessageConverter<?>> converters) {
messageConverters.add(createXmlHttpMessageConverter());
messageConverters.add(
new
MappingJackson2HttpMessageConverter());
super
.configureMessageConverters(converters);
}
private
HttpMessageConverter<Object> createXmlHttpMessageConverter() {
MarshallingHttpMessageConverter xmlConverter =
new
MarshallingHttpMessageConverter();
XStreamMarshaller xstreamMarshaller =
new
XStreamMarshaller();
xmlConverter.setMarshaller(xstreamMarshaller);
xmlConverter.setUnmarshaller(xstreamMarshaller);
return
xmlConverter;
}
}
|
Note that the XStream library now needs to be present on the classpath.
Also be aware that by extending this support class, we are losing the default message converters which were previously pre-registered – we only have what we define.
Let’s go over this example – we are creating a new converter – theMarshallingHttpMessageConverter – and we’re using the Spring XStream support to configure it. This allows a great deal of flexibility since we’re working with the low level APIs of the underlying marshalling framework – in this case XStream – and we can configure that however we want.
We can of course now do the same for Jackson – by defining our ownMappingJackson2HttpMessageConverter we can now set a custom ObjectMapper on this converter and have it configured as we need to.
In this case XStream was the selected marshaller/unmarshaller implementation, but others like CastorMarshaller can be used to – refer to Spring api documentation for full list of available marshallers.
At this point – with XML enabled on the back end – we can consume the API with XML Representations:
1
|
curl --header
"Accept: application/xml"
http:
//localhost
:8080
/spring-rest/foos/1
|
As well as with the server side, Http Message Conversion can be configured in the client side on the Spring RestTemplate.
We’re going to configure the template with the 「Accept」 and 「Content-Type」 headers when appropriate and we’re going to try to consume the REST API with full marshalling and unmarshalling of the Foo Resource – both with JSON and with XML.
1
2
3
4
5
6
7
|
@Test
public
void
testGetFoo() {
String URI = 「http:
//localhost:8080/spring-rest/foos/{id}";
RestTemplate restTemplate =
new
RestTemplate();
Foo foo = restTemplate.getForObject(URI, Foo.
class
,
"1"
);
Assert.assertEquals(
new
Integer(
1
), foo.getId());
}
|
Let’s now explicitly retrieve the Resource as an XML Representation – we’re going to define a set of Converters – same way we did previously – and we’re going to set these on the RestTemplate.
Because we’re consuming XML, we’re going to use the same XStream marshaller as before:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@Test
public
void
givenConsumingXml_whenReadingTheFoo_thenCorrect() {
String URI = BASE_URI +
"foos/{id}"
;
RestTemplate restTemplate =
new
RestTemplate();
restTemplate.setMessageConverters(getMessageConverters());
HttpHeaders headers =
new
HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_XML));
HttpEntity<String> entity =
new
HttpEntity<String>(headers);
ResponseEntity<Foo> response =
restTemplate.exchange(URI, HttpMethod.GET, entity, Foo.
class
,
"1"
);
Foo resource = response.getBody();
assertThat(resource, notNullValue());
}
private
List<HttpMessageConverter<?>> getMessageConverters() {
XStreamMarshaller marshaller =
new
XStreamMarshaller();
MarshallingHttpMessageConverter marshallingConverter =
new
MarshallingHttpMessageConverter(marshaller);
converters.add(marshallingConverter);
|
1
2
3
|
List<HttpMessageConverter<?>> converters =
new
ArrayList<HttpMessageConverter<?>>();
return
converters;
}
|
Similarly, let’s now consume the REST API by asking for JSON:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@Test
public
void
givenConsumingJson_whenReadingTheFoo_thenCorrect() {
String URI = BASE_URI +
"foos/{id}"
;
RestTemplate restTemplate =
new
RestTemplate();
restTemplate.setMessageConverters(getMessageConverters());
HttpHeaders headers =
new
HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity =
new
HttpEntity<String>(headers);
ResponseEntity<Foo> response =
restTemplate.exchange(URI, HttpMethod.GET, entity, Foo.
class
,
"1"
);
Foo resource = response.getBody();
assertThat(resource, notNullValue());
}
private
List<HttpMessageConverter<?>> getMessageConverters() {
List<HttpMessageConverter<?>> converters =
new
ArrayList<HttpMessageConverter<?>>();
converters.add(
new
MappingJackson2HttpMessageConverter());
return
converters;
}
|
Finally, let’s also send JSON data to the REST API and specify the media type of that data via the Content-Type header:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@Test
public
void
givenConsumingXml_whenWritingTheFoo_thenCorrect() {
String URI = BASE_URI +
"foos/{id}"
;
RestTemplate restTemplate =
new
RestTemplate();
restTemplate.setMessageConverters(getMessageConverters());
Foo resource =
new
Foo(
4
,
"jason"
);
HttpHeaders headers =
new
HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.setContentType((MediaType.APPLICATION_XML));
HttpEntity<Foo> entity =
new
HttpEntity<Foo>(resource, headers);
ResponseEntity<Foo> response =
restTemplate.exchange(URI, HttpMethod.PUT, entity, Foo.
class
, resource.getId());
Foo fooResponse = response.getBody();
Assert.assertEquals(resource.getId(), fooResponse.getId());
}
|
What’s interesting here is that we’re able to mix the media types – we are sending XML data but we’re waiting for JSON data back from the server. This shows just how powerful the Spring conversion mechanism really is.
In this tutorial, we looked at how Spring MVC allows us to specify and fully customize Http Message Converters to automatically marshall/unmarshall Java Entities to and from XML or JSON. This is of course a simplistic definition, and there is so much more that the message conversion mechanism can do – as we can see from the last test example.
We have also looked at how to leverage the same powerful mechanism with theRestTemplate client – leading to a fully type-safe way of consuming the API.