Skip to main content

Content negotiation in Spring Boot

In situations where an API is being consumed by multiple systems, you might be required to provide the response in different formats depending on the client. One client might accept JSON while the other can only work with XML. That’s where the Request Content Negotiation comes into picture. In this post, we’ll explore a specific usecase of the request content negotiation where a client sends their preferences for the mediatype of their choice.

Setup

The examples in this post use

  • Java 18
  • Spring Boot 2.7.1
  • Maven 3.8.6
  • curl (to send HTTP requests)
  • xmlstarlet (to format xml)
  • jq (to format json)

Spring supports the following strategies for the content negotiation.

Suffix Pattern matching (deprecated)

Suffix Pattern matching was historically used when an HTTP client wasn’t able to send proper Accept request headers. For example, a request GET /game/trial.xml matches with @GetMapping("/game/trial") and the response is an XML document.

Suffix pattern matching is now deprecated and turned off by default. The reasoning behind the deprecation is explained in detail in the docs here.

Query Parameter mapping

Query Parameter mapping is the preferred way to handle content negotiation when the HTTP clients can’t send proper Accept request headers. For example, a request GET /game/trial?format=xml matches to @GetMapping("/game/trial") and the response is an XML document.

To enable this, open application.yml (or application.properties) of the Spring project and add the following configuration.

yml
spring:
  mvc:
    contentnegotiation:
      favor-parameter: true

By default, format is the query parameter to pass the acceptable format. Here’s an example to get the response in XML.

sh
curl -s http://localhost:8080/game/trial?format=xml | xml fo -o
<Game>
	<title>Control</title>
	<publisher>Remedy Entertainment</publisher>
</Game>

The default response format is JSON (unless specified / configured otherwise).

sh
curl -s http://localhost:8080/game/trial | jq
{
	"title": "Control",
	"publisher": "Remedy Entertainment"
}

A different parameter name can also be configured through the following configuration.

yml
spring:
  mvc:
    contentnegotiation:
      favor-parameter: true
      parameter-name: mediatype

Spring supports a lot of media types out-of-the-box. But custom media types can also be configured as follows.

yml
spring:
  mvc:
    contentnegotiation:
      favor-parameter: true
      parameter-name: mediatype
      media-types.toml: application/toml

To support custom media types, you might have to provide additional dependencies and converters to Spring.

Accept header field

Sending the Accept request headers is the default content negotiation strategy provided by Spring.

sh
curl -s http://localhost:8080/game/trial -H 'Accept: application/xml' | xml fo -o
<Game>
	<title>Control</title>
	<publisher>Remedy Entertainment</publisher>
</Game>

Similarly, a request for JSON response returns:

sh
curl -s http://localhost:8080/game/trial -H 'Accept: application/json' | jq
{
	"title": "Control",
	"publisher": "Remedy Entertainment"
}

Source code

Related