Configuring content-negotiation (JSON, XML, and so on)

In this recipe, we will see how to configure the way we want the system to decide which format to render depending upon the client expectations.

Getting ready

We are mostly going to review the XML configuration here. Then, we will test the API with different requests to ensure support is provided to the XML format.

How to do it...

  1. The RequestMappingHandlerAdapter configuration has been altered in dispatcher-context.xml. A contentNegotiationManager property has been added, as well as an xmlConverter bean:
    <bean class="org.sfw.web...
      method.annotation.RequestMappingHandlerAdapter">
      <property name="messageConverters">
        <list>
          <ref bean="xmlConverter"/>
          <ref bean="jsonConverter"/>
          </list>
      </property>
      <property name="customArgumentResolvers">
        <list>
          <bean class="net.kaczmarzyk.spring.data.jpa. web.SpecificationArgumentResolver"/>
        <bean class="org.sfw.data.web. PageableHandlerMethodArgumentResolver">
          <property name="pageParameterName" value="pn"/>
          <property name="sizeParameterName" value="ps"/>
          </bean>
        </list>
      </property>
      <property name="requireSession" value="false"/>
      <property name="contentNegotiationManager" ref="contentNegotiationManager"/>
    </bean>
      
    <bean id="contentNegotiationManager" class="org.sfw.web.accept. ContentNegotiationManagerFactoryBean">
      <property name="favorPathExtension" value="true" />
      <property name="favorParameter" value="false" />
      <property name="ignoreAcceptHeader" value="false"/>
      <property name="parameterName" value="format" />
      <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>
    <bean id="xmlConverter" class="org.sfw.http...xml.MarshallingHttpMessageConverter">
      <property name="marshaller">
        <ref bean="xStreamMarshaller"/>
      </property>
      <property name="unmarshaller">
        <ref bean="xStreamMarshaller"/>
      </property>
    </bean>
    <bean id="xStreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
      <property name="autodetectAnnotations" value="true"/>
    </bean>
  2. A Maven dependency has been added to XStream as follows:
        <dependency>
          <groupId>com.thoughtworks.xstream</groupId>
           <artifactId>xstream</artifactId>
          <version>1.4.3</version>
        </dependency>
  3. Calling the URL: http://localhost:8080/api/indices/EUROPE/^GDAXI/histo.json should target the getHistoIndex() handler the same way as before and you should receive the same json response:
    How to do it...
  4. Also, calling the URL http://localhost:8080/api/indices/EUROPE/^GDAXI/histo.xml should now generate the following XML formatted response:
    How to do it...

How it works...

We have added support for XML using the MarshallingHttpMessageConverter bean, defined a default media type (application/json), and defined a global content negotiation strategy.

Support for XML marshalling

As we said in the previous recipe, MarshallingHttpMessageConverter comes with the framework, but it requires the spring-oxm dependency, as well as a definition for a marshaller and unmarshaller. spring-oxm is the Maven artefact to reference here:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-oxm</artifactId>
  <version>${spring.version}</version>
</dependency>

The XStream marshaller

We chose XStreamMarshaller as the provider for the XML marshalling operations:

<bean class="org.springframework.oxm.xstream.XStreamMarshaller">
  <property name="autodetectAnnotations" value="true"/>
</bean>

The XStream marshaller is part of the spring-oxm project. Even if it is not recommended for external source parsing (which is not what we intend to do), it is very good and requires very few configuration by default (no specific class registration or initial mapping strategy required).

Types and fields can be annotated to customize the default behavior. You can find some examples here from their documentation:

  • @XStreamAlias: Used on the type, field, or attribute
  • @XStreamImplicit: Used in collections or arrays
  • @XStreamAsAttribute: Used to mark a field as an attribute
  • @XStreamConverter: Targets a specific converter for the field

In our case, we have applied a minimal marshalling customization in DTOs.

You can find more information about XStream on their official website: http://xstream.codehaus.org.

Negotiation strategies with ContentNegotiationManager

Here, we are talking about the way we configure the system to choose one media type over another, for responses. The client shows expectations in its request and the server tries to satisfy them at best from the available resolutions.

There are three ways for the client to specify its media type expectations. We discuss them in the following sections.

The Accept header

The client request specifies a mime type or a list of mime types (application/json, application/xml, and so on) as a value of the Accept header. It is the default choice for Spring MVC.

Web browsers can send various Accept headers though, and it would be risky to rely entirely on these headers. Therefore, it is good to support at least one alternative.

These headers can even be completely ignored with the ignoreAcceptHeader Boolean property in ContentNegotiationManager.

The file extension suffix in the URL path

Allowing the specification of a file extension suffix in the URL path is one alternative. It is the discriminator option in our configuration.

The favorPathExtension Boolean property in ContentNegotiationManager has been set to true for this purpose and our AngularJS factories actually request .json paths.

The request parameter

You can define a specific query parameter if you dislike the path extension option. The default name of this parameter is format. It is customizable with the parameterName property, and the potential expected values are the registered format suffixes (xml, html, json, csv, and so on).

This option can be set as the discriminator option with the favorParameter Boolean property.

Java Activation Framework

Setting the useJaf Boolean property to true configures to rely on the Java Activation Framework, rather than Spring MVC itself, for the suffix to-media type mappings (json to correspond to application/json, xml to correspond to application/xml, and so on).

@RequestMapping annotations as ultimate filters

Finally, the controller with the @RequestMapping annotations and especially the produces attribute should have the final word on which format will be rendered.

There's more...

Now we will look at the implementation of JAXB2 as an XML parser and the ContentNegotiationManagerFactoryBean configuration.

Using a JAXB2 implementation as an XML parser

JAXB2 is the current Java specification for XML bindings. Our example with XStream was just an example and another XML marshaller can of course be used. Spring supports JAXB2. It even provides a default JAXB2 implementation in the spring-oxm package: org.springframework.oxm.jaxb.Jaxb2Marshaller.

Using JAXB2 annotations in DTOs is probably a better choice for portability. Visit the Jaxb2Marshaller JavaDoc for more details about its configuration: http://docs.spring.io/autorepo/docs/spring/4.0.4.RELEASE/javadoc-api/org/springframework/oxm/jaxb/Jaxb2Marshaller.html.

The ContentNegotiationManagerFactoryBean JavaDoc

The full possible configuration for ContentNegotiationManagerFactoryBean is accessible again in its JavaDoc:

http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.html

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

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