Mapping extension content types in Apache CXF JAX-RS

Go To StackoverFlow.com

5

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.

2012-04-03 20:15
by Garret Wilson


4

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

2012-04-27 11:55
by praseodym
You mean in the section where it says, "Here's an example of how it can be done with Spring" - Garret Wilson 2012-04-27 16:37
Oops, hadn't noticed that it's only for Spring - praseodym 2012-04-28 12:17
Although it wasn't what the OP was looking for, it was very helpful for me, and I'm not using Spring either. Thanks - Brian Topping 2012-09-04 10:31


1

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.

2016-06-21 22:23
by Bill


0

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.

2012-04-06 21:21
by Donal Fellows
Thanks for the tip. Your response may prove useful---but I'm afraid it doesn't answer the question. : - Garret Wilson 2012-04-07 22:50


0

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

2013-10-31 11:17
by thibr