Spring MVC Content Negotiation XML JSON

This tutorial shows you how to use Spring MVC Content Negotiation XML JSON features. According to wikipedia, “content negotiation is a mechanism defined in the HTTP specification that makes it possible to serve different versions of a document at the same URI, so that user agents/users can specify which version fit their capabilities the best.”

We show you how to negotiate between XML and JSON using HTTP Accept header, via URL file extension, a specific request parameter or fallback on a default content type when nothing of the previous is requested.

Maven Dependency

We use Apache Maven to manage our project dependencies. Add the following dependencies so Maven can automatically manage the dependencies. Make sure the com.fasterxml.jackson.dataformat:jackson-dataformat-xml resides on the classpath for XML marshalling.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.memorynotfound.spring.mvc</groupId>
    <artifactId>xml-view-resolver</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>SPRING-MVC - ${project.artifactId}</name>
    <url>http://memorynotfound.com</url>
    <packaging>war</packaging>

    <properties>
        <encoding>UTF-8</encoding>
        <spring.version>4.2.6.RELEASE</spring.version>
    </properties>

    <dependencies>
        <!-- spring libraries -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- xml -->
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
            <version>2.7.4</version>
        </dependency>

        <!-- servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Spring MVC Content Negotiation XML JSON – Configuration

When we extend from the WebMvcConfigurerAdapter we can override some convinient methods for managing the content negotiation manager.

The configureContentNegotiation(ContentNegotiationConfigurer configurer) method – as the name implies – configures the content negotiation. We can register a default content type that’ll be served if none of the other content negotiation properties are requested.

The configureViewResolvers(ViewResolverRegistry registry) method registers different view resolvers that the application can serve. In this example we register both the MappingJackson2XmlView for serving XML and the MappingJackson2JsonView for serving JSON.

package com.memorynotfound.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import org.springframework.web.servlet.view.xml.MappingJackson2XmlView;

@EnableWebMvc
@Configuration
@ComponentScan("com.memorynotfound")
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer
                .defaultContentType(MediaType.APPLICATION_JSON_UTF8)
                .parameterName("type")
                .favorParameter(true)
                .ignoreUnknownPathExtensions(false)
                .ignoreAcceptHeader(false)
                .useJaf(true);
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.enableContentNegotiation(
                new MappingJackson2XmlView(),
                new MappingJackson2JsonView());
    }

}

Here you can find the equivallent Spring MVC XML Configuration.

<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/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">

    <mvc:annotation-driven/>
    <context:component-scan base-package="com.memorynotfound" />

    <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
        <property name="defaultContentType" value="APPLICATION_JSON_UTF8"/>
        <property name="parameterName" value="type"/>
        <property name="favorParameter" value="true"/>
        <property name="ignoreUnknownPathExtensions" value="false"/>
        <property name="ignoreAcceptHeader" value="false"/>
        <property name="useJaf" value="true"/>
    </bean>

    <mvc:view-resolvers>
        <mvc:content-negotiation>
            <mvc:default-views>
                <bean class="org.springframework.web.servlet.view.xml.MappingJackson2XmlView"/>
                <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
            </mvc:default-views>
        </mvc:content-negotiation>
    </mvc:view-resolvers>

</beans>

We need to register the DispatcherServlet which acts like a front controller for mapping the requests to the correct endpoint. We pass the previous configuration file to the servlet using the getServletConfigClasses() method.

package com.memorynotfound.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class ServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { WebConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }
}

Creating a Rest Endpoint

First, we create a simple POJO which will be returned as a response.

package com.memorynotfound.model;

import java.util.Date;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Course {

    private Integer id;
    private String name;
    private Date date;

    public Course() {
    }

    public Course(Integer id, String name, Date date) {
        this.id = id;
        this.name = name;
        this.date = date;
    }

    public Integer getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public Date getDate() {
        return date;
    }
}

Next, we created a rest endpoint which will return an instance of the previous Course class. Note that this code has no logic to return a specific view. The view resolution is managed by the content negotiation manager, which we configured earlier.

package com.memorynotfound.controller;

import com.memorynotfound.model.Course;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.Date;

@Controller
@RequestMapping("/courses")
public class CourseController {

    @RequestMapping(value="/{id}", method = RequestMethod.GET)
    public Course getCourse(@PathVariable Integer id, ModelMap model) {
        return new Course(id, "Spring MVC Content Negotiation XML Example", new Date());
    }
}

Demo

Time for a simple demo, the following curl command passes a HTTP “Accept: application/json” header. This means we would like to accept JSON.

curl -H "Accept: application/json" http://localhost:8081/spring-mvc-xml-view-resolver/courses/2

The previous curl command will return the following response.

{
    "course":{
        "id":2,
        "name":"Spring MVC Content Negotiation XML Example",
        "date":1463573802406
    }
}

The following example demonstrates that when we pass a file extention to the path parameter or if we pass a special request parameter of type XML, we receive the response in XML format.

URL: http://localhost:8081/spring-mvc-xml-view-resolver/courses/2?type=xml or http://localhost:8081/spring-mvc-xml-view-resolver/courses/2.xml

spring mvc content negotiation xml json example

References

Download

You may also like...