JAX-RS offers a wonderful way to specify content types in @Produces
, and the framework will automatically determine the best content type from the client's HTTP Accept
header and, wonder of wonders, even convert your object to that type (e.g. XML using JAXB or JSON using Jackson) when returning information to the caller.
My (work) client, as clients often do, made a simple job more difficult by requesting I specify the content type by the extension in the URL, e.g. api/widgets.json
. This would force me to have various getWidgetsXXX()
methods, one with @Produces("application/json")
, another with @Produces("application/xml")
, etc.
But I'm using Apache CXF and I was delighted to find that I could configure CXF to map various extensions to content types using the jaxrs.extensions
init parameter!
<!-- registers extension mappings -->
<init-param>
<param-name>jaxrs.extensions</param-name>
<param-value>
xml=application/xml
json=application/json
</param-value>
</init-param>
But I can find absolutely no documentation on how this works in the real world. I naively thought I could just annotate a method with a path with an extension and it would mimic the Accepts
header:
@Path("/widgets.{extension}")
@GET
@Produces({ "application/json", "application/xml" })
public List<Widget> getWidgets();
So I call it using api/widgets.json
, and it returns XML! Which is particularly odd, because JAX-RS specifies that the default content type is the first one listed.
Where can I find out how to use CXF extension content type mapping?
P.S. I am not using Spring.
Adding the following in your <jaxrs:server>
works:
<jaxrs:extensionMappings>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</jaxrs:extensionMappings>
Source: http://cxf.apache.org/docs/jax-rs.html#JAX-RS-Debugging
Don't know whether that help you or not but I was also facing the same issue to introduce something like that in my JAX-RS services. I achieved this functionality using JAX-RS_Content_Negotiation Following location has details about it.
https://docs.jboss.org/resteasy/docs/3.0.6.Final/userguide/html/JAX-RS_Content_Negotiation.html
You just have to map your media types with the values which you want
<context-param>
<param-name>resteasy.media.type.mappings</param-name>
<param-value>
html : text/html, json : application/json, xml :
application/xml
</param-value>
</context-param>
@GET
@Path("/second/{param}")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response printStudent(@PathParam("param") String msg) {
}
now i can access my services like that and response is according the extension which i put at the end
http://localhost:8080/RESTfulExample/rest/message/second/bill.json
you can put .xml OR .json at the end of the url and service will generate response accordingly.
In your situation, I'd declare that the method @Produces the content type */*
(i.e., a full wildcard) and then do the content negotiation myself. You'd probably be looking at a method signature like this:
@javax.ws.rs.GET
@javax.ws.rs.Path("{filename}")
@javax.ws.rs.Produces("*/*")
javax.ws.rs.core.Response getDirectoryOrFileContents(
@javax.ws.rs.PathParam("filename") String filename,
@javax.ws.rs.core.Context javax.ws.rs.core.HttpHeaders headers);
That gives you access to both the desired filename — one way to guess the media type to deliver — and the full set of HTTP headers (hint: use headers.getAcceptableMediaTypes()
), which give the other way. How to balance the two is likely to be “interesting”. (The code I've got to do it is very specific to my app's internal model, so isn't likely to be useful to you.) You then return the result by constructing a Response
, which gives you quite close control over what the client gets back.
Yes, this is more work than letting CXF handle all this for you (it normally generates a lot of boilerplate to do all of this stuff) but in a complex case you'll be glad of the control.
The extension mimics the Accept
header as you guessed. However you must not declare the extenstion in the @Path
annotation:
@Path("/widgets")
@GET
@Produces({ "application/json", "application/xml" })
public List<Widget> getWidgets();
You can then call widgets.xml
or widgets.json