In the HTTP protocol, when a client initiates an HTTP request, it can carry a request header Accept to tell the server which response types the client can accept (MIME), either one or more. It is now common for front- and back-end separation to use this.

1
Accept:application/json

For Spring MVC framework to accept the corresponding Accept will be based on a certain policy to find the corresponding HttpMessageConverter to handle the format of the response data. So we just need to find a way to specify Accept dynamically.

Content Negotiation

Spring MVC provides a mechanism called content negotiation, where the client declares the required MIME type at the time of request, and the server just needs to configure some policies to implement an interface that returns a different MIME type of data format, requiring JSON format to respond to JSON data, need XML format to return XML data.

Spring MVC version based on Spring MVC 5.3.9 .

Server-side configuration of content negotiation

The configuration of content negotiation is handled by the ContentNegotiationManager in Spring MVC and we can configure it via the ContentNegotiationConfigurer.

The first step is to add Jackson’s XML processing library to the Spring MVC project.

1
2
3
4
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

Then configure the content negotiation configuration in WebMvcConfigurer.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorParameter(true)
                // The client request url needs to submit a query parameter, the default name is format
                .parameterName("format")
                // If you don't declare the query parameter, it returns json, and you need to declare it if you want to specify the default return type
                // .defaultContentType(MediaType.APPLICATION_XML)
                .mediaType("xml", MediaType.APPLICATION_XML)
                .mediaType("json", MediaType.APPLICATION_JSON);
    }
}

This declares that the client request interface should carry a query parameter (the parameter name defaults to format, which you can modify) to specify the MIME designation. According to the above configuration, if you need to return JSON:

1
https://yourapi?format=json

You can also not carry the format parameter, because the default is JSON, and to change the default MIME type you need to call defaultContentType.

If you need to return XML:

1
https://yourapi?format=xml

The server-side interface also requires a simple modification.

1
2
3
4
5
6
7
8
@GetMapping(value = "/get",produces = {"application/json","application/xml"})
public Map<String, String> doGet(@RequestParam String foo, String bar) {

    Map<String, String> map = new HashMap<>();
    map.put("foo", foo);
    map.put("bar", bar);
    return map;
}

It is very important to declare the corresponding produce in @RequestMapping or its simplification annotation according to the configuration. This way we have very few changes to make, can adapt to more scenarios, and is simple to maintain.

Other strategies

In fact, Spring MVC content negotiation can also be achieved through suffix extensions such as /yourapi.json or /yourapi.xml. There is also the possibility of declaring MIME types directly in the client request headers. These are not very convenient to operate so I will not introduce them, if you are interested, you can go to the official documentation.

Reference https://felord.cn/contentNegotiationManager.html