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.
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.
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>
XStream
as follows:<dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.3</version> </dependency>
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:http://localhost:8080/api/indices/EUROPE/^GDAXI/histo.xml
should now generate the following XML formatted response:We have added support for XML using the MarshallingHttpMessageConverter
bean, defined a default media type (application/json
), and defined a global content negotiation strategy.
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>
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 fieldIn 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.
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 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
.
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.
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.
Now we will look at the implementation of JAXB2 as an XML parser and the ContentNegotiationManagerFactoryBean
configuration.
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.