Content Negotiation in Spring Framework
1. Introduction
With what we had done with BeanNameViewResolver, is that we just had created multiple bean views in Spring context to generate the expected output. Spring soon introduced the Content Negotiation strategy, where we can use the traditional RESTful @ResponseBody approach and HTTP message converters, to return desired outputs in JSON or XML, along with some flexibility and dynamicity it provides with the configuration.
Often we need to provide multiple representations (or views) of the same data returned by the controller. This is very elegantly achieved with Content negotiation in Spring.
2. Implementation
To quickly start with the implementation, lets first create a new maven project. Click here to get help on creating your new maven project in Eclipse
Let’s now add the needed dependencies to the pom.xml file.
pom.xml
<!-- Spring dependencies --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.2.1.RELEASE</version> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.2.6</version> </dependency> <dependency> <groupId>javax.xml</groupId> <artifactId>jaxb-impl</artifactId> <version>2.1</version> </dependency> <!-- Jackson JSON Processor --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.4.1</version> </dependency>
Open web.xml and make sure you have the proper configuration done.
web.xml
<?xml version="1.0" encoding="ISO-8859-1"?> <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_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>SpringMVCWithContentNegotiation</display-name> <servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
Create a new context file as <servlet name>-servlet.xml, which has to be mvc-dispatcher-servlet.xml with the following content.
mvc-dispatcher-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"> <mvc:path-matching registered-suffixes-only="true" /> </mvc:annotation-driven> <context:component-scan base-package="com.jcombat.controller" /> <!-- To disable path extension check in the path variable of URI --> <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"> <property name="favorPathExtension" 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> <!-- For Other media types --> <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> </beans>
Note that the component-scanning is applied only to com.jcombat.controller package, so Spring can auto-detect the application controller.
When making an HTTP request, we can specify what type of response we would like to have by setting the Accept header property. But due to improper working of the browsers with Accept headers, we mostly prefer ignoring the Accept headers in a Spring based web applications, which returns output in formats other than the HTML.
With Java Activation Framework (JAF), we don’t need to explicitly specify the media type mappings, instead it gets implicitly done by JAF. To use JAF, we might need to add an extra dependency of activation.jar. Probably we just want to support two output formats i.e. JSON and XML, therefore we are opting out of it and rather specifying the media type mappings explicitly.
It’s now time to write the controller, which should be like specified below.
EmployeeController.java
package com.jcombat.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.jcombat.bean.Employee; @Controller public class EmployeeController { @RequestMapping(value = "/employeeData", method = RequestMethod.GET, produces={"application/xml", "application/json"}) public @ResponseBody Employee getEmployee() { Employee employee = new Employee(); employee.setEmailId("abc@gmail.com"); employee.setEmpId("123"); employee.setName("Ajit"); return employee; } }
3. Useful Links
4. Running the application
When we run the application, below is what we see.
JSON Output
XML Output
5. Download the source code
Reference: | Content Negotiation in Spring Framework from our JCG partner Abhimanyu Prasad at the jCombat blog. |