Using ContentNegotiatingViewResolver

Content negotiation is a mechanism that makes it possible to serve different representations of the same resource. For example, so far we have shown our product detail page in a JSP representation. What if we want to represent the same content in an XML format. Similarly, what if we want the same content in a JSON format? Here comes Spring MVC's ContentNegotiatingViewResolver (org.springframework.web.servlet.view.ContentNegotiatingViewResolver) to help us.

The XML and JSON formats are popular data interchange formats that are heavily used in web service communications. Using ContentNegotiatingViewResolver, we can incorporate many Views such as MappingJacksonJsonView (for JSON) and MarshallingView (for XML) to represent the same product information in a XML or JSON format.

Time for action - configuring ContentNegotiatingViewResolver

ContentNegotiatingViewResolver does not resolve Views itself, but rather delegates to other view resolvers based on the request. Now, let's add a content negotiation capability to our application:

  1. Open pom.xml, which you can find under the project root directory.
  2. You should be able to see some tabs under pom.xml; select the Dependencies tab and click on the Add button of the Dependencies section.
  3. A Select Dependency window will appear; in Group Id enter org.springframework, in Artifact Id enter spring-oxm, in Version enter 4.3.0.RELEASE, select Scope as compile, then click on the OK button.
  4. Add another dependency: org.codehaus.jackson  as Group Id, jackson-mapper-asl as Artifact Id, 1.9.10 as Version, and select Scope as compile. Then click on the OK button.
  5. Similarly, add one more dependency: com.fasterxml.jackson.core as Group Id, jackson-databind as Artifact Id , 2.8.0 as Version, and select Scope as compile. Then click on the OK button and save pom.xml.
  6. Now add the bean configuration in our web application context configuration (WebApplicationContextConfig.java) for the JSON View as follows:
          @Bean 
          public MappingJackson2JsonView jsonView() { 
             MappingJackson2JsonView jsonView = new       
          MappingJackson2JsonView(); 
             jsonView.setPrettyPrint(true); 
        
             return jsonView;  
          } 
    
  7. Add another bean configuration for the XML View as follows:
          @Bean 
          public MarshallingView xmlView() { 
             Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); 
             marshaller.setClassesToBeBound(Product.class); 
        
             MarshallingView xmlView = new MarshallingView(marshaller); 
             return xmlView; 
          } 
    
  8. Finally, add the bean configuration for ContentNegotiatingViewResolver in our WebApplicationContextConfig web application context configuration file as follows:
          @Bean 
          public ViewResolver contentNegotiatingViewResolver( 
                   ContentNegotiationManager manager) { 
             ContentNegotiatingViewResolver resolver = new       
          ContentNegotiatingViewResolver(); 
             resolver.setContentNegotiationManager(manager); 
           
             ArrayList<View>   views = new ArrayList<>(); 
             views.add(jsonView()); 
             views.add(xmlView()); 
           
             resolver.setDefaultViews(views); 
              
             return resolver; 
          } 
    
  9. Open our product domain class (Product.java) and add the @XmlRootElement annotation at the top of the class.
  10. Similarly, add the @XmlTransient annotation at the top of the getProductImage() method and add another annotation @JsonIgnore on top of the productImage field.
  11. Now run our application and enter the URL http://localhost:8080/webstore/market/product?id=P1235. You will now be able to view the details page of the product with the ID P1234.
  12. Now change the URL to the .xml extension: http://localhost:8080/webstore/market/product.xml?id=P1235. You should be able to see the same content in the XML format as shown in the following screenshot:
    Time for action - configuring ContentNegotiatingViewResolver

    The product details page showing product information in the XML format

  13. Similarly, this time change the URL to the .json extension: http://localhost:8080/webstore/market/product.json?id=P1235. You will be able to see the JSON representation of that content as shown in the following screenshot:
    Time for action - configuring ContentNegotiatingViewResolver

    The product details page showing the product information in the JSON format

What just happened?

Since we want an XML representation of our model data to convert our model objects into XML, we need Spring's object/XML mapping support—that's why we added the dependency for spring-oxm.jar through steps 1 to 3. The spring-oxm notation will help us convert an XML document to and from a Java object.

Similarly, to convert model objects into JSON, Spring MVC will use jackson-mapper-asl.jar and jackson-databind.jar; thus we need those jars in our project as well. In steps 4 and 5, we just added the dependency configuration for those jars.

If you remember in our servlet context (servlet-context.xml), we already defined InternalResourceViewResolver as our view resolver to resolve JSP-based Views, but this time we want a view resolver to resolve XML and JSON Views. That's why in step 8 we configured ContentNegotiatingViewResolver (org.springframework.web.servlet.view.ContentNegotiatingViewResolver) in our servlet context.

As I already mentioned, ContentNegotiatingViewResolver does not resolve Views itself but rather it delegates to other Views based on the request, so we need to introduce other Views to ContentNegotiatingViewResolver. How we do that is through the setDefaultViews method in ContentNegotiatingViewResolver:

@Bean 
public ViewResolver contentNegotiatingViewResolver( 
         ContentNegotiationManager manager) { 
   ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver(); 
   resolver.setContentNegotiationManager(manager); 
    
   ArrayList<View>   views = new ArrayList<>(); 
   views.add(jsonView()); 
   views.add(xmlView()); 
    
   resolver.setDefaultViews(views); 
       
   return resolver; 
} 

To configure bean references for jsonView and xmlView inside ContentNegotiatingViewResolver, we need a bean definition for those references. That is the reason we defined those beans in steps 6 and 7.

The xmlView bean configuration especially has one important property to be set called classesToBeBound; this lists the domain objects that require XML conversion during the request processing. Since our product domain object requires XML conversion, we added com.packt.webstore.domain.Product to the list classesToBeBound:

@Bean 
public MarshallingView xmlView() { 
   Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); 
   marshaller.setClassesToBeBound(Product.class); 
    
   MarshallingView xmlView = new MarshallingView(marshaller); 
   return xmlView; 
} 

In order to convert it to XML, we need to give MarshallingView one more hint to identify the root XML element in the Product domain object. This is why in step 9 we annotated our class with the @XmlRootElement (javax.xml.bind.annotation.XmlRootElement) annotation.

In step 10, we added the @XmlTransient (javax.xml.bind.annotation.XmlTransient) annotation on top of the getProductImage () method and added another annotation—@JsonIgnore (org.codehaus.jackson.annotate.JsonIgnore)—on top of the productImage field. This is because we don't want to represent the product image as part of the XML View or the JSON View since both formats are purely a text-based representation, and it is not possible to represent images in text.

In step 10, we simply accessed our product details page in the regular way by firing the web request (http://localhost:8080/webstore/products/product?id=P1234) from the browser. We could see the normal JSP View, as expected.

In step 11, we just changed the URL slightly by adding an .xml extension to the request path: http://localhost:8080/webstore/market/products/product.xml?id=P1235. This time we were able to see the same product information in the XML format.

Similarly for the JSON View, we changed the extension for the .json path to http://localhost:8080/webstore/market/products/product.json?id=P1235 and we were able to see the JSON representation of the same product information.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.35.179