Spring MVC Content Negotiation Tutorial with Example


When making a request via HTTP it is possible to specify what type of response you would like by setting the Accept header property. Web browsers have this preset to request HTML. In fact, if you look, you will see that browsers actually send very confusing Accept headers, which makes relying on them impractical.

So, for those situations where the Accept header property is not desirable, Spring offers some conventions to use instead. You can configure a content negotiation strategy centrally once and it will apply wherever different formats (media types) need to be determined.

Enabling Content Negotiation in Spring MVC

Spring supports a couple of conventions for selecting the format required: URL suffixes and/or a URL parameter. These work alongside the use of Accept headers. As a result, the content-type can be requested in any of three ways. By default they are checked in this order:

Add a path extension (suffix) in the URL

If the incoming URL is something like http://localhost:8080/SpringContentNegotiation/api/users.html then HTML is required. For a spreadsheet the URL should be http://localhost:8080/SpringContentNegotiation/api/users.xls.

The suffix to media-type mapping is automatically defined via the JavaBeans Activation Framework or JAF (so activation.jar must be on the class path).

A URL parameter

If the incoming URL supplies request parameter something like http://myserver/myapp/accounts/list?format=xls. The name of the parameter is format by default, but this may be changed. Using a parameter is disabled by default, but when enabled, it is checked second.

Accept HTTP header property is checked

This is how HTTP is actually defined to work, but, as previously mentioned, it can be problematic to use.

When using XML configuration, the content negotiation strategy is most easily setup via the ContentNegotiationManagerFactoryBean:

<!--
 Setup a simple strategy: 
 1. Take all the defaults.
 2. Return XML by default when not sure. 
 -->
 <bean id="contentNegotiationManager"
 class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
 <property name="defaultContentType" value="application/xml"
/>
 </bean>

 <!-- Make this available across all of Spring MVC -->
 <mvc:annotation-driven
content-negotiation-manager="contentNegotiationManager" />

The ContentNegotiationManager created by either setup is an implementation of ContentNegotationStrategy that implements the PPA Strategy (path extension, then parameter, then Accept header).

Additional Configuration Options

the strategy can be configured using methods on the factory bean:

<!-- Total customization - see below for explanation. -->
 <bean id="contentNegotiationManager"
 class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
 <property name="favorPathExtension" value="false" />
 <property name="favorParameter" value="true" />
 <property name="parameterName" value="format" />
 <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>

what it is mean:
  • Disabled path extension. Note that favor does not mean use one approach in preference to another, it just enables or disables it. The order of checking is always path extension, parameter, Accept header.
  • Enable the use of the URL parameter but instead of using the default parameter, format, we can use any Parameter Name instead.
  • Ignore the Accept header completely. This is often the best approach if most of your clients are actually web-browsers (typically making REST calls via AJAX).
  • Don't use the JAF, instead specify the media type mappings manually - we only wish to support JSON and XML.

Listing Employees Example



Below is the example To return a list of employees in JSON or XML. We will ignore the HTML generating methods for now.

STEP 1:- Open Eclipse and Create Dynamic Web Project named SpringContentNegotiationExample

STEP 2:- Make sure you use Target Runtime as Apache Tomcat 7.0. 

STEP 3:- copy below jars to WEB-INF/lib folder.
  • commons-logging-1.2.jar
  • jackson-annotations-2.6.0.jar
  • jackson-core-2.6.0.jar
  • jackson-databind-2.6.0.jar
  • spring-aop-4.1.4.RELEASE.jar
  • spring-beans-4.1.4.RELEASE.jar
  • spring-context-4.1.4.RELEASE.jar
  • spring-core-4.1.4.RELEASE.jar
  • spring-expression-4.1.4.RELEASE.jar
  • spring-web-4.1.4.RELEASE.jar
  • spring-webmvc-4.1.4.RELEASE.jar
STEP 4:- Create Spring Configuration Bean file. /WebContent/WEB-INF/dispatcher-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="
 http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/mvc 
 http://www.springframework.org/schema/mvc/spring-mvc.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context.xsd">
 
 <context:component-scan base-package="com.tutorialsdesk" />
 
 <bean id="contentNegotiationManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
 <property name="favorPathExtension" value="true" />
 <property name="favorParameter" value="true" />
 <property name="parameterName" value="mediaType" />
 <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>
 
 <mvc:annotation-driven
content-negotiation-manager="contentNegotiationManager" />
 
</beans>

In the above file we have configured property favorPathExtension to true, so system will process URL like Employees.json.

STEP 5 :- Map Spring MVC in /WebContent/WEB-INF/web.xml file as below :-

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
 <display-name>SpringContentNegotiationExample</display-name>
 <welcome-file-list>
 <welcome-file>index.html</welcome-file>
 </welcome-file-list>
 <servlet>
 <servlet-name>dispatcher</servlet-name>
 <servlet-class>
 org.springframework.web.servlet.DispatcherServlet
 </servlet-class>
 <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
 <servlet-name>dispatcher</servlet-name>
 <url-pattern>/*</url-pattern>
 </servlet-mapping>
</web-app>

STEP 6 :- Create Controller Class.
  • Package: com.tutorialsdesk.controller
  • Filename: EmployeeController.java
package com.tutorialsdesk.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.tutorialsdesk.model.Employee;

@RestController
public class EmployeeController {
 @RequestMapping(value = "/Employees", method = RequestMethod.GET)
 @ResponseStatus(HttpStatus.OK)
 public List<Employee> getCountries(){ 
 List<Employee> listOfEmployees = new ArrayList<Employee>(); 
 listOfEmployees=createEmployeeList(); 
 return listOfEmployees; 
 } 

 public List<Employee> createEmployeeList(){ 
 List<Employee> listOfEmployees = new ArrayList<Employee>(); 
 listOfEmployees.add(new Employee(29,
"Bill","Smith","bill@tutorialsdesk.com")); 
 listOfEmployees.add(new Employee(30,
"Tom","Moore","tom@gmail.com")); 
 listOfEmployees.add(new Employee(35,
"Sachin","Sharma","sachin@yahoo.com")); 
 listOfEmployees.add(new Employee(25,
"Martin","Jung","mj@hotmail.com")); 
 return listOfEmployees; 
 } 
}


STEP 7 :- Create model class.
  • Package: com.tutorialsdesk.model
  • Filename: Employee.java
package com.tutorialsdesk.model;

import java.io.Serializable;

import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlAccessType;

@XmlRootElement (name = "employee")
@XmlAccessorType(XmlAccessType.NONE)
public class Employee implements Serializable{

 private static final long serialVersionUID = 5088863992478607917L;

 @XmlAttribute
 private Integer id;
 
 @XmlElement
 private String firstName;
 
 @XmlElement
 private String lastName;
 
 @XmlElement
 private String email;

 public Employee(Integer id, String firstName, String lastName, String email) {
 super();
 this.id = id;
 this.firstName = firstName;
 this.lastName = lastName;
 this.email = email;
 }

 public Employee() {
 super();
 }

 public Integer getId() {
 return id;
 }

 public void setId(Integer id) {
 this.id = id;
 }

 public String getFirstName() {
 return firstName;
 }

 public void setFirstName(String firstName) {
 this.firstName = firstName;
 }

 public String getLastName() {
 return lastName;
 }

 public void setLastName(String lastName) {
 this.lastName = lastName;
 }

 public String getEmail() {
 return email;
 }

 public void setEmail(String email) {
 this.email = email;
 }

 public static long getSerialversionuid() {
 return serialVersionUID;
 }

 @Override
 public String toString() {
 return "Employee [id=" + id + ", firstName=" + firstName
 + ", lastName=" + lastName + ", email=" + email + "]";
 }
 
}


Provided We have JAXB2 and Jackson on my classpath, Spring MVC will automatically setup the necessary HttpMessageConverters. Our domain classes must also be marked up with JAXB2 and Jackson annotations to enable conversion.

STEP 8 :- Run your project enter below URL in your browser

localhost:8080/SpringContentNegotiationExample/Employees.json
or
localhost:8080/SpringContentNegotiationExample/Employees.xml
or
localhost:8080/SpringContentNegotiationExample/Employees.xml
http://localhost:8080/SpringContentNegotiationExample/Employees?format=json
or
http://localhost:8080/SpringContentNegotiationExample/Employees?format=xml

Combining Data and Presentation Formats

Spring MVC’s REST support builds on the existing MVC Controller framework. So it is possible to have the same web-applications return information both as raw data (like JSON) and using a presentation format (like HTML).

Both techniques can easily be used side by side in the same controller, like this:
@RestController
public class EmployeeController {
 @RequestMapping(value = "/Employees", method = RequestMethod.GET,
produces={"application/xml", "application/json"})
 @ResponseStatus(HttpStatus.OK)
 public List<Employee> getEmployeesJSONXML(){ 
 List<Employee> listOfEmployees = new ArrayList<Employee>(); 
 listOfEmployees=createEmployeeList(); 
 return listOfEmployees; 
 } 
 @RequestMapping(value = "/Employees", method = RequestMethod.GET)
 
 public String getEmployeesView(Model model){ 
 List<Employee> listOfEmployees = new ArrayList<Employee>(); 
 listOfEmployees=createEmployeeList(); 
 model.addAttribute("empList",listOfEmployees );
 return "listEmployee"; 
 } 
 public List<Employee> createEmployeeList(){ 
 List<Employee> listOfEmployees = new ArrayList<Employee>(); 
 listOfEmployees.add(new Employee(29,
"Bill","Smith","bill@tutorialsdesk.com")); 
 listOfEmployees.add(new Employee(30,
"Tom","Moore","tom@gmail.com")); 
 listOfEmployees.add(new Employee(35,
"Sachin","Sharma","sachin@yahoo.com")); 
 listOfEmployees.add(new Employee(25,
"Martin","Jung","mj@hotmail.com")); 
 return listOfEmployees; 
 } 
}



Keep visiting TutorialsDesk for more tutorials and practical programming examples on Spring MVC. Hope we are able to explain you Spring MVC Content Negotiation Example, if you have any questions or suggestions please write to us using contact us form.(Second Menu from top left). Please share us on social media if you like the tutorial.
SHARE
    Blogger Comment
    Facebook Comment