If we develop REST using Spring MVC, it will support XML and JSON data. I have wrote ContentNegotiationViewResorver in my spring config bean app-servlet.xml
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"
p:order="1">
<property name="mediaTypes">
<map>
<entry key="xml" value="application/xml" />
<entry key="json" value="application/json" />
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<property name="marshaller">
<bean class="org.springframework.oxm.xstream.XStreamMarshaller"
p:autodetectAnnotations="true" />
</property>
</bean>
<bean
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
</bean>
And my spring REST Controller is:
@Controller
@RequestMapping("/rest/customers")
class CustomerRestController {
protected Log log = LogFactory.getLog(CustomerRestController.class);
@RequestMapping(method = POST)
@ResponseStatus(CREATED)
public void createCustomer(@RequestBody Customer customer,
HttpServletResponse response) {
log.info(">>>" + customer.getName());
response.setHeader("Location", String.format("/rest/customers/%s",
customer.getNumber()));
}
@RequestMapping(value = "/{id}", method = GET)
@ResponseBody
public Customer showCustomer(@PathVariable String id) {
Customer c = new Customer("0001", "teddy", "bean");
return c;
}
@RequestMapping(value = "/{id}", method = PUT)
@ResponseStatus(OK)
public void updateCustomer(@RequestBody Customer customer) {
log.info("customer: " + customer.getName());
}
I set @XStreamAlias("customer")
annotation in my customer domain class.
But when I try access http://localhost:8080/rest/customers/teddy.xml
it always response JSON data.
I set @XmlRootElement(name="customer")
annotation in my customer domain class.
But when I try access http://localhost:8080/rest/customers/teddy.json
it always response XML data.
Is there some thing wrong ?
Well I got a solution but I don't know if it's the right way in your method show customer:
In this part, we are using MVC of spring and in the controller we should be return a view, so I removed the annotation
@ResponseBody
and I return aString
with the name of the view because in our XML we added aContentNegotiatingViewResolver
and when we haveResponseBody
the contentnegociationviewresolver is ignored because is waiting for a view but we returned the object so the method should be like that:well that works for me, if you have problems you can add to your
app-servlet.xml
this bean, but I don't think that you have to add this.
I got the answers from mkyong.com
I think "xml" content type should be mapped to "text/xml" not to "application/xml". Also, to force content type resolvers based on extension, you can try to set the "favorPathExtension" property of "ContentNegotiatingViewResolver" to true(though it should have been true by default!)
EDIT: I have now added a working sample at this GIT location -
git://github.com/bijukunjummen/mvc-samples.git
, if you bring up the endpoint, using mvn tomcat:run, the json is served athttp://localhost:8080/mvc-samples/rest/customers/teddy.json
and xml athttp://localhost:8080/mvc-samples/rest/customers/teddy.xml
. This uses JAXB2 not XStream, as I am familiar with JAXB. One thing I noticed was that when my JAXB annotations were not correct in Customer class, Spring was serving out JSON and not XML the way you saw it(You can replicate it by removing the XMLRootElement annotation from Customer class), once I fixed up my annotations, I got back XML as expected. So it could be that there is something wrong with your XStream configuration.EDIT 2: You are right!! I did not notice, once I got back xml, I assumed that json is working now. I see the problem, in
AnnotationMethodHandlerAdapter
, the handling for@ResponseBody
is a little strange, it completely ignores the ViewResolvers, and uses the registered MessageConverters instead completely bypassing theContentNegotiatingViewResolver
, one workaround for now is to use@ModelAttribute
annotation for response, instead of @ResponseBody, this way the view Resolvers are getting called. Try now using the project atgit@github.com:bijukunjummen/mvc-samples.git
and see if it works for you. This could be a Spring bug, you may try and bring it up in the Spring forum and see what they recommend.Accessing the controller using a browswer will send a typical browser Accept header. It will not match any view resolver and default to the first one (application/xml) or it matches because application/xml is in the Accept list.
I can recommend using RestClient http://code.google.com/p/rest-client/ to have complete control over what Accept header (if one at all) you want to send.
I don't recommend using text/xml as the default character set is US-ASCII and not UTF-8. This might create funky encoding problems down the road. You can always specify the encoding but appliation/xml has a UTF-8 default encoding.
Spring 3.1 solves the problem you mention using the new
produces
element on the@RequestMapping
annotation. This allows you to control theHttpMessageConverter
that Spring applies to your object.I wrote a blog post about it:
http://springinpractice.com/2012/02/22/supporting-xml-and-json-web-service-endpoints-in-spring-3-1-using-responsebody/
What Accept headers are sent to your server? Make sure the content type you would like to request is in this list.
I had the same problem. I assume you're using Spring 3 and you've used
<mvc:annotation-driven/>
. I'm not entirely sure, but I think this creates some conflict based on the message converters that the mvc namespace configures.Using the oxm namespace worked for me:
Content Configuration (mvc and internal view resolver are in another context):
This example uses JAXB, so you'd need jaxb-api and jaxb-impl on the classpath.
Also, just a tip, you don't need the app-servlet.xml. In your web.xml, set the config to null and let the Context Listener load them for you: