Spring Content Negotiation

This post describes content negotiation in Spring MVC project. We will cover different techniques about Spring Content Negotiation.

 

Introduction

There are 2 ways for generating output in our Spring MVC application.

  • For RESTful API, use @ResponseBody annotation. Spring MVC HTTP message converters will return data in the required format (e.g. JSON, XML etc.).
  • For the traditional applications, viewResolver used to generate presentation format like HTML.

There is a third possibility which requires both RESTful and traditional web-based data.

For above use cases, it’s desire to know what kind of data format expected in the request body and what it expects in the HTTP response. Spring MVC uses ContentNegotationStrategy to determine what format requested by the user.

 

1. How Contents Negotiation Work?

While working on the HTTP request, there are certain ways to perform the content negotiation. One of the most common way in Spring content negotiation is the use of the Accept header property.Client API sets the Accept header to specify the response it expecting.Spring provides certain conventions to make this content negotiation more flexible in case the Accept header is missing or not properly configured.

Read our article on Content Negotiation for more detail.

 

2. Spring Content Negotiation

Spring support following content negotiation strategy for determining media or content type of request.

  • URL path extension (suffix) in the request
    • If URL is https://javadevjournal.com/v1/customers.json than JSON is required.
    • If URL ending with .xml than XML is required
  • URL parameter in the request e.g https://javadevjournal.com/customers?format=json
  • Accept header in the request.

[pullquote align=”normal”]By default content negotiation works in the same hierarchy as described above.We can customize it based on our need. [/pullquote]

 

3. Content Negotiation Strategies

Before we look into different content negotiation strategies by Spring, let’s do the basic setup by adding required dependencies in pom.xml.

For this post, we will use JSON and XML representation.

<dependencies>
   <!-- for XML support -->
   <dependency>
      <groupId>com.fasterxml.jackson.dataformat</groupId>
      <artifactId>jackson-dataformat-xml</artifactId>
      <version>2.9.0</version>
   </dependency>
   <!-- for Jackson support -->
   <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.3</version>
   </dependency>
   <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.3</version>
   </dependency>
</dependencies>

 

Spring Boot Configurations

If you are building your application using Spring Boot, please keep in mind following points

  1. Spring Boot provides Jackson dependency using Spring Boot parent POM.

You are free to override version defined in the parent pom using the <exclusion> option in the pom.xml file. pom.xml in Spring Boot will be like 

<dependencies>
   <!-- for XML support -->
   <dependency>
      <groupId>com.fasterxml.jackson.dataformat</groupId>
      <artifactId>jackson-dataformat-xml</artifactId>
    </dependency>
   <!-- nothing for Jackson -->
</dependencies>

 

3.1 URL Suffix Strategy

This spring content negotiation strategy  check for the extension (suffix) in the URL to determine the desired output content type.

3.1.1 Java Configuration

Here is our Java configuration for URL suffix based strategy

@Configuration
@EnableWebMvc
public class RestApiApplication extends WebMvcConfigurerAdapter {

@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {

    configurer.favorParameter(false).
    ignoreAcceptHeader(false).
    defaultContentType(MediaType.APPLICATION_XML).
    mediaType("xml", MediaType.APPLICATION_XML).
    mediaType("json", MediaType.APPLICATION_JSON);
    }
}
3.1.2 XML Configuration
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
   <property name="favorPathExtension" value="true" />
   <property name="favorParameter" value="false" />
   <property name="ignoreAcceptHeader" value="true" />
   <property name="useJaf" value="false" />
   <property name="defaultContentType" value="application/xml" />
   <property name="mediaTypes">
      <map>
         <entry key="json" value="application/json" /&gt;
         <entry key="xml" value="application/xml" />
      </map>
   </property>
</bean>
<!-- Make this available across all of Spring MVC -->
 <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />

Let’s check what we did in the configurations

  • Turned off parameter and Accept header based content negotiation.
  • We are setting up XML as default content type.
  • Support both JSON and XML format.

If we run the application,

curl http://localhost:8080/api/rest/customer

Here is the output

<Customer>
   <firstName>Java</firstName>
   <lastName>Devjournal</lastName>
   <age>34</age>
   <email><code class=""language-json”">contact-us@wordpress-241348-2978695.cloudwaysapps.com</email> <account> <accountId>12</accountId> <accountName>Demo</accountName> <balance>3456.0</balance> </account> </Customer> 

As the contetType default to XML, system returned XML data in response.If we use JSON extension

curl http://localhost:8080/api/rest/customer.json

Response body

{  
   "firstName":"Java",
   "lastName":"Devjournal",
   "age":34,
   "email":"[email protected]",
   "account":{  
      "accountId":12,
      "accountName":"Demo",
      "balance":3456.0
   }
}

 

3.2 URL Parameter Strategy

Spring MVC content negotiation also support parameter based strategy. Spring MVC a check for the format parameter in the request to find media type.

3.2.1 Java Configuration
@Configuration
@EnableWebMvc
public class RestApiApplication extends WebMvcConfigurerAdapter {

@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {

   configurer.favorPathExtension(false).
   favorParameter(true).
   ignoreAcceptHeader(false).
   useJaf(false).
   defaultContentType(MediaType.APPLICATION_XML).
   mediaType("xml", MediaType.APPLICATION_XML).
   mediaType("json", MediaType.APPLICATION_JSON);
}
3.2.2 XML Configuration
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
   <property name="favorPathExtension" value="false" />
   <property name="favorParameter" value="true" />
   <property name="ignoreAcceptHeader" value="true" />
   <property name="useJaf" value="false" />
   <property name="defaultContentType" value="application/json" />
   <property name="mediaTypes">
      <map>
         <entry key="json" value="application/json" />
         <entry key="xml" value="application/xml" />
      </map>
   </property>
</bean>
<!-- Make this available across all of Spring MVC -->
 <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />

Let’s check what we did in the configurations

  • Turn off the extension/ suffix and Accept header based content negotiation.
  • We are setting up XML as default content type.
  • Support both JSON and XML format.

If we run the application, 

curl http://localhost:8080/api/rest/customer

Here is the output

<Customer>
   <firstName>Java</firstName>
   <lastName>Devjournal</lastName>
   <age>34</age>
   <email><code class=""language-json”">contact-us@wordpress-241348-2978695.cloudwaysapps.com</email> <account> <accountId>12</accountId> <accountName>Demo</accountName> <balance>3456.0</balance> </account> </Customer> 
curl http://localhost:8080/api/rest/customer?format=json

Response body

{  
   "firstName":"Java",
   "lastName":"Devjournal",
   "age":34,
   "email":"[email protected]",
   "account":{  
      "accountId":12,
      "accountName":"Demo",
      "balance":3456.0
   }
}
3.2.3 Change Parameter Name

The name of the parameter is format by default.Spring provides a way to change this parameter.

configurer.parameterName("customParameter")

XML Configuration

..<property name="parameterName" value="mediaType" />..

 

3.3 The Accept Header Strategy

If Accept header is active, Spring MVC look for this header value in the incoming request for the content type. Let’s check the process to enable this approach

3.3.1 Java Configuration
@Configuration
@EnableWebMvc
public class RestApiApplication extends WebMvcConfigurerAdapter {

@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {

   configurer.favorPathExtension(false).
   favorParameter(false).
   ignoreAcceptHeader(false).
   useJaf(false).
   defaultContentType(MediaType.APPLICATION_XML).
   mediaType("xml", MediaType.APPLICATION_XML).
   mediaType("json", MediaType.APPLICATION_JSON);
}
3.3.2 XML Configuration
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
   <property name="favorPathExtension" value="false" />
   <property name="favorParameter" value="false" />
   <property name="ignoreAcceptHeader" value="false" />
   <property name="useJaf" value="false" />
   <property name="defaultContentType" value="application/json" />
   <property name="mediaTypes">
      <map>
         <entry key="json" value="application/json" />
         <entry key="xml" value="application/xml" />
      </map>
   </property>
</bean>
<!-- Make this available across all of Spring MVC -->
 <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />

Let’s check what we did in the configurations

  • Turn off the extension/ suffix and parameter based content negotiation.
  • Enabled Accept header negotiation.
  • We are setting XML as default content type.
  • Support both JSON and XML format.

 

Summary

In this post, We discussed Spring Content Negotiation strategies. We covered 3 different strategies provided by Spring along with options to customize these strategies.You can find the source code on the GitHub

2 thoughts on “Spring Content Negotiation”

  1. You did not show the execution of Accept header option . Because there is obstacle in getting it working. Even though you set the ignoreAcceptHeader(true) it will bounce 406 on the face. If you add jackson-dataformat-xml dependency , the xml and json both will work . But if you’re using @ControllerAdvice to catch the global exceptions you will have difficult time to get all working.

Comments are closed.