Spring MVC 4 - ContentNegotiatingViewResolver example

Posted on September 16, 2017


In Spring MVC framework, the ContentNegotiatingViewResolver resolver class is used to resolve the other views such as PDF, EXCEL, JSON, XML views etc. based on the URL extension or Accept header or query parameter.

By default Spring MVC application check the URL extension first to resolve the request media type, and then Accept header and query parameter are checked. Default content type will be used, if none of these are enable.

You can configure the content negotiation options by overriding the configureContentNegotiation() method of the WebMvcConfigurerAdapter in your web @Configuration class.

In this post, I will show you how to use the ContentNegotiatingViewResolver in Spring web application to resolve the following request URLs.

Request URLView Resolver
http://localhost:8080/student.pdfUsing the AbstractPdfView to render the PDF document. Example
http://localhost:8080/student.xlsxUsing the AbstractXlsxView to render the Excel document. Example
http://localhost:8080/student.xmlUsing the MarshallingView to render the XML document. Example
http://localhost:8080/student.jsonUsing the MappingJackson2JsonView to render the JSON document. Example
http://localhost:8080/student.rssUsing the AbstractRssFeedView to render the RSS document. Example
http://localhost:8080/studentUsing the InternalResourceViewResolver to render the JSP file. Example

Tools and technologies used for this application are-

  • Spring Web MVC 4.3.10.RELEASE
  • Spring OXM 4.3.10.RELEASE
  • Jackson API 2.8.7
  • Apache POI API 3.9
  • iText API 2.1.7
  • ROME RSS API 1.8.0           
  • Java SE 1.8
  • Maven 3.3.9
  • Eclipse Neon.3
  • Apache Tomcat 7.0.47

Let’s see the complete example to demonstrate the above.

Project structure

Review the following web project structure build using Maven build tool.

Spring-mvc-content-negotiating.png

Related - How to create a web project using maven build tool in eclipse IDE.

Jar dependencies

Edit pom.xml file of your maven project and add the following dependencies in it.

<dependencies>
  <!-- Spring MVC Dependency -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>4.3.10.RELEASE</version>
  </dependency>

  <!-- Spring XML Marshalling Dependency -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-oxm</artifactId>
    <version>4.3.10.RELEASE</version>
  </dependency>

  <!-- Jackson API for JSON -->
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.8.7</version>
  </dependency>
  <dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.8.7</version>
  </dependency>

  <!-- Apache POI API for Excel -->
  <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.9</version>
  </dependency>

  <!-- iText API for PDF -->
  <dependency>
    <groupId>com.lowagie</groupId>
    <artifactId>itext</artifactId>
    <version>2.1.7</version>
  </dependency>
  
  <!-- ROME API for RssFeed -->
  <dependency>
    <groupId>com.rometools</groupId>
    <artifactId>rome</artifactId>
    <version>1.8.0</version>
  </dependency>
  
  <!-- JSTL Dependency -->
  <dependency>
    <groupId>javax.servlet.jsp.jstl</groupId>
    <artifactId>javax.servlet.jsp.jstl-api</artifactId>
    <version>1.2.1</version>
  </dependency>
  <dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
  </dependency>

  <!-- Servlet Dependency -->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
  </dependency>

  <!-- JSP Dependency -->
  <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.1</version>
    <scope>provided</scope>
  </dependency>
</dependencies>

Model class

Create a Student.java class under com.boraji.tutorial.spring.model package. We will use it for exposing model data to different views.

Annotate this class with @XmlRootElement annotation, which is required by the Jaxb2Marshaller to map the java object to XML element.

Student.java

package com.boraji.tutorial.spring.model;

import java.time.LocalDate;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "student")
public class Student {
   private int id;
   private String name;
   private String email;
   private LocalDate dob;
   
   // Getter and Setter methods
}

 

PDF view

To create a PDF document view in Spring MVC application, you need to extend the AbstractPdfView class as follows. 

StudentPdfView.java

package com.boraji.tutorial.spring.view;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.view.document.AbstractPdfView;

import com.boraji.tutorial.spring.model.Student;
import com.lowagie.text.Document;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;

public class StudentPdfView extends AbstractPdfView {

   @Override
   protected void buildPdfDocument(Map<String, Object> model, Document doc, 
         PdfWriter writer, HttpServletRequest req,
         HttpServletResponse res) throws Exception {

      Student student = (Student) model.get("student");

      //column widths
      float[] columnWidths = { 1.5f, 2f};
      PdfPTable table = new PdfPTable(columnWidths);
      table.setWidthPercentage(90f);
      
      //Table header
      PdfPCell cell = null;
      cell = new PdfPCell(new Phrase("ID"));
      table.addCell(cell);
      cell = new PdfPCell(new Phrase(Integer.toString(student.getId())));
      table.addCell(cell);
      cell = new PdfPCell(new Phrase("NAME"));
      table.addCell(cell);
      cell = new PdfPCell(new Phrase(student.getName()));
      table.addCell(cell);
      cell = new PdfPCell(new Phrase("DOB"));
      table.addCell(cell);
      cell = new PdfPCell(new Phrase(student.getDob().toString()));
      table.addCell(cell);
      cell = new PdfPCell(new Phrase("EMAIL"));
      table.addCell(cell);
      cell = new PdfPCell(new Phrase(student.getEmail()));
      table.addCell(cell);
      
      doc.add(table);
   }

}

Now create a PDF view resolver by implementing the ViewResolver.

StudentPdfViewResolver.java

package com.boraji.tutorial.spring.view;

import java.util.Locale;

import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;

public class StudentPdfViewResolver implements ViewResolver {

   @Override
   public View resolveViewName(String viewName, Locale locale) throws Exception {
      StudentPdfView pdfView = new StudentPdfView();
      return pdfView;
   }
}

Excel view

To create an excel document view in Spring MVC application, you need to extend the AbstractXlsView, AbstractXlsxView or AbstractXlsxStreamingView class. In this example we will use the AbstractXlsxView class for MS Office 2007 XLSX format as follows.

StudentExcelView.java

package com.boraji.tutorial.spring.view;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.servlet.view.document.AbstractXlsxView;

import com.boraji.tutorial.spring.model.Student;

public class StudentExcelView extends AbstractXlsxView {

   @Override
   protected void buildExcelDocument(Map<String, Object> model, Workbook wb, HttpServletRequest req,
         HttpServletResponse res) throws Exception {

      Student student = (Student) model.get("student");
      Sheet sheet;
      sheet = wb.createSheet("Students");

      int rowCount = 1;
      // SID
      Row row = sheet.createRow((short) rowCount++);
      Cell cell = row.createCell(0);
      cell.setCellValue("ID");
      cell = row.createCell(1);
      cell.setCellValue(Integer.toString(student.getId()));

      // Name
      row = sheet.createRow((short) rowCount++);
      cell = row.createCell(0);
      cell.setCellValue("Name");
      cell = row.createCell(1);
      cell.setCellValue(student.getName());

      // DOB
      row = sheet.createRow((short) rowCount++);
      cell = row.createCell(0);
      cell.setCellValue("DOB");
      cell = row.createCell(1);
      cell.setCellValue(student.getDob().toString());

      // Email
      row = sheet.createRow((short) rowCount++);
      cell = row.createCell(0);
      cell.setCellValue("Email");
      cell = row.createCell(1);
      cell.setCellValue(student.getDob().toString());
   }
}

Now create an excel view resolver by implementing the ViewResolver.

StudentExcelViewResolver.java

package com.boraji.tutorial.spring.view;

import java.util.Locale;

import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;

public class StudentExcelViewResolver implements ViewResolver {

   @Override
   public View resolveViewName(String viewName, Locale locale) throws Exception {
      StudentExcelView excelView = new StudentExcelView();
      return excelView;
   }
}

XML view

You can use the MarshallingView view class for rendering the XML document in Spring MVC application. This view uses a Marshaller to map the java object to XML element.

Now, we need to create an XML view resolver using the MarshallingView view to resolve the XML document request. So, create an XML view resolver by implementing the ViewResolver as follows. 

StudentXmlViewResolver.java

package com.boraji.tutorial.spring.view;

import java.util.Locale;

import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.xml.MarshallingView;

import com.boraji.tutorial.spring.model.Student;

public class StudentXmlViewResolver implements ViewResolver {

   @Override
   public View resolveViewName(String viewName, Locale locale) throws Exception {

      Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
      marshaller.setClassesToBeBound(Student.class);
      
      MarshallingView view = new MarshallingView();
      view.setMarshaller(marshaller);
      view.setModelKey("student");
      return view;
   }
}

JSON view

You can use the MappingJackson2JsonView view class for rendering the JSON document in Spring application. This view uses the Jackson 2's ObjectMapper for converting the model object to JSON.

Now create a JSON view resolver using the MappingJackson2JsonView view as follows.

StudentJsonViewResolver.java

package com.boraji.tutorial.spring.view;

import java.util.Locale;

import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

public class StudentJsonViewResolver implements ViewResolver {

   @Override
   public View resolveViewName(String viewName, Locale locale) throws Exception {
      MappingJackson2JsonView view = new MappingJackson2JsonView();

      //Formatting date
      ObjectMapper mapper=new ObjectMapper();
      mapper.registerModule(new JavaTimeModule());
      mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
      view.setObjectMapper(mapper);
      
      return view;
   }
}

RssFeed View

To create a RSS document view in Spring MVC application, you can extend the AbstractRssFeedView class. The AbstractRssFeedView uses the ROME API for RSS and Atom feeds.

StudentRssFeedView.java

package com.boraji.tutorial.spring.view;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.view.feed.AbstractRssFeedView;

import com.boraji.tutorial.spring.model.Student;
import com.rometools.rome.feed.rss.Channel;
import com.rometools.rome.feed.rss.Content;
import com.rometools.rome.feed.rss.Item;

public class StudentRssFeedView extends AbstractRssFeedView {

   @Override
   protected void buildFeedMetadata(Map<String, Object> model, Channel feed, HttpServletRequest req) {
      feed.setTitle("BORAJI.COM");
      feed.setLink("https://www.boraji.com/");
      feed.setDescription("This feed has been created using ROME.");
   }

   @Override
   protected List<Item> buildFeedItems(Map<String, Object> model, HttpServletRequest request,
         HttpServletResponse response) throws Exception {
      Student student = (Student) model.get("student");

      List<Item> items = new ArrayList<>();
      Item item = new Item();
      item.setTitle("BORAJI.COM");
      item.setLink("https://www.boraji.com/");
      item.setPubDate(new Date());
      Content content = new Content();
      content.setType("text/html");
      content.setValue("<table>" + 
            "<tr><th>ID</th><td>" + student.getId() + "</td></tr>" + 
            "<tr><th>NAME</th><td>" + student.getName() + "</td></tr>" + 
            "<tr><th>DOB</th><td>" + student.getDob() + "</td></tr>"+ 
            "<tr><th>EMAIL</th><td>" + student.getEmail() + "</td></tr>" + 
            "</table>");

      item.setContent(content);
      items.add(item);
      return items;
   }
}

Now create a RSS view resolver by implementing the ViewResolver.

package com.boraji.tutorial.spring.view;

import java.util.Locale;

import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;

public class StudentRssFeedViewResolver implements ViewResolver {

   @Override
   public View resolveViewName(String viewName, Locale locale) throws Exception {
      StudentRssFeedView view = new StudentRssFeedView();
      return view;
   }
}

JSP View

Create a new folders as \WEB-INF\views under src\main\webapp folder. Now, create index.jsp and student.jsp files under src\main\webapp\WEB-INF\views under folder.

index.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
  pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>BORAJI.COM</title>
</head>
<body>
  <h2>Spring MVC 4 - ContentNegotiatingViewResolver example</h2>
  <h5>Download Student profile in -</h5>
  <ul>
    <li><a href="student.pdf">PDF</a></li>
    <li><a href="student.xlsx">EXCEL</a></li>
    <li><a href="student.xml">XML</a></li>
    <li><a href="student.json">JSON</a></li>
    <li><a href="student.rss">RSS</a></li>
    <li><a href="student">HTML</a></li>
  </ul>
</body>
</html>

student.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
  pageEncoding="ISO-8859-1"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>BORAJI.COM</title>
</head>
<body>
  <h2>Spring MVC 4 - ContentNegotiatingViewResolver example</h2>
  <table border="1" style="width: 400px;">
    <tr>
      <th>ID</th>
      <td>${student.id}</td>
    </tr>
    <tr>
      <th>NAME</th>
      <td>${student.name}</td>
    </tr>
    <tr>
      <th>DOB</th>
      <td>${student.dob}</td>
    </tr>
    <tr>
      <th>EMAIL</th>
      <td>${student.email}</td>
    </tr>
  </table>
</body>
</html>

 

Spring web configuration + Servlet container initialization

Create a web @Configuration class by extending the WebMvcConfigurerAdapter class and annotating with @EnableWebMvc and @ComponentScan.

Declare the ContentNegotiatingViewResolver bean method to resolve the different views and override the configureContentNegotiation() method of the WebMvcConfigurerAdapter class to configure the content negotiation options.

Note - You must set the ContentNegotiationManager of the ContentNegotiatingViewResolver class after configuring the content negotiation options inside the configureContentNegotiation() method.

WebConfig.java

package com.boraji.tutorial.spring.config;

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

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

import com.boraji.tutorial.spring.view.StudentExcelViewResolver;
import com.boraji.tutorial.spring.view.StudentJsonViewResolver;
import com.boraji.tutorial.spring.view.StudentPdfViewResolver;
import com.boraji.tutorial.spring.view.StudentRssFeedViewResolver;
import com.boraji.tutorial.spring.view.StudentXmlViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.boraji.tutorial.spring.controller" })
public class WebConfig extends WebMvcConfigurerAdapter {

   // Customize the content negotiation options
   @Override
   public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
      configurer.defaultContentType(MediaType.TEXT_HTML)
      .favorPathExtension(true)
      .favorParameter(false)
      .ignoreAcceptHeader(true);
   }
   
   // Setting different views using  the ContentNegotiatingViewResolver
   @Bean
   public ContentNegotiatingViewResolver viewResolver(ContentNegotiationManager cnManager) {

      ContentNegotiatingViewResolver cnResolver = new ContentNegotiatingViewResolver();
      cnResolver.setContentNegotiationManager(cnManager);

      // Excel View Resolver
      StudentExcelViewResolver excelResolver = new StudentExcelViewResolver();

      // PDF View Resolver
      StudentPdfViewResolver pdfResolver = new StudentPdfViewResolver();

      // XML View Resolver
      StudentXmlViewResolver xmlResolver = new StudentXmlViewResolver();

      // JSON View Resolver
      StudentJsonViewResolver jsonResolver = new StudentJsonViewResolver();

      // RSS FEED view resolver
      StudentRssFeedViewResolver feedResolver = new StudentRssFeedViewResolver();

      // JSP View Resolver
      InternalResourceViewResolver jspResolver = new InternalResourceViewResolver(
            "/WEB-INF/views/", ".jsp");

      // Add views to list
      List<ViewResolver> resolvers = new ArrayList<>();
      resolvers.add(jspResolver);
      resolvers.add(excelResolver);
      resolvers.add(pdfResolver);
      resolvers.add(xmlResolver);
      resolvers.add(jsonResolver);
      resolvers.add(feedResolver);
      
      cnResolver.setViewResolvers(resolvers);
      return cnResolver;
   }
}

Create a container initializer class by extending the AbstractAnnotationConfigDispatcherServletInitializer to bootstrap the Spring MVC application.

MyWebAppInitializer.java

package com.boraji.tutorial.spring.config;

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

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

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

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

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

Controller class

Create a @Controller class under com.boraji.tutorial.spring.controller package to handler the PDF, Excel, JSON, RSS, XML and JSON requests as follows.

StudentController.java

package com.boraji.tutorial.spring.controller;

import java.time.LocalDate;
import java.time.Month;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import com.boraji.tutorial.spring.model.Student;

@Controller
public class StudentController {

   @GetMapping("/")
   public String index() {
      return "index";
   }

   // Handler method to render the PDF, EXCEL, JSON, RSS and JSP views
   @GetMapping("/student")
   public String getStudent(Model model) {

      Student student = new Student();
      student.setId(100);
      student.setName("Mary Elizabeth Smith");
      student.setDob(LocalDate.of(1990, Month.AUGUST, 25));
      student.setEmail("[email protected]");

      model.addAttribute("student", student);
      return "student";
   }

}

Build + Deploy + Run application

Use the following maven commands to build, package, deploy and run application.

mvn clean install  (This command triggers war packaging)

mvn tomcat7:run (This command run embedded tomcat and deploy war file automatically)

You can refer this link to learn how to run the above commands in Eclipse IDE.

 

Type the following URLs in browser's address bar to test the different views.

http://localhost:8080/

Spring-mvc-content-negotiating-01.png

http://localhost:8080/student.pdf

Spring-mvc-content-negotiating-02.png

http://localhost:8080/student.xlsx

Spring-mvc-content-negotiating-03.png

http://localhost:8080/student.xml

Spring-mvc-content-negotiating-04.png

http://localhost:8080/student.json

Spring-mvc-content-negotiating-05.png

http://localhost:8080/student.rss

Spring-mvc-content-negotiating-06.png

http://localhost:8080/student

Spring-mvc-content-negotiating-07.png