I am enjoying the auto HTTP content negotiation of JAX-RS (specifically Jersey), i.e. its ability to route my resources by "Accept" and/or "Content-Type" headers. But I'm finding that sometimes it doesn't give me enough control when there is a conflict.
For example, consider the following endpoints:
@Path("/order")
public class OrderController {
@GET
@Path("{orderID: \\d+}")
@Produces("text/html")
public View getOrderView(@PathParam("orderID") long id) {
Order order = this.getOrderData(id);
return new OrderView(order);
}
@GET
@Path("{orderID: \\d+}")
@Produces({"application/json", "application/xml"})
public Order getOrderData(@PathParam("orderID") long id) {
return new OrderService.findOrder(id);
}
}
I will get different results between Firefox and Chrome. Firefox will map to the HTML endpoint while Chrome will trigger the XML endpoint when I navigate each to the endpoint URL. The difference between them is the order of the listed MIME types in their Accept headers. Chrome sends the following:
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.107 Safari/534.13
Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Versus in Firefox it lists HTML first:
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Seems logical that it would match the first entry when all are weighted the same. But in my case I am getting the different results than I want so it would be nice to determine a better method for tie-breaking.
My question: short of injecting header information into these methods and performing the media type processing myself, is there a way to "tweak the weights" so to speak in the event of a tie? For instance, can I tell it to always trump XML with HTML? My RESTful clients are very explicit about what type they want back but browsers are notoriously sloppy with Accept headers. (Personally I think they should weight HTML slightly above XML as this is what users expect but it's a little late for that.)
Alternatively, can I perform my own custom content negotiation only once in some centralized location? I'm not opposed to writing this logic out manually but not if it means applying it to every single instance of my resources. Does JAX-RS have some concept of adding a filter to the pipeline to tweak requests before they are routed?
There is a mechanism in Jersey to override the relative degree of preference from the HTTP Accept header. Just add a parameter "qs" to the @Produces annotation that you want to take precedence. In your case:
@Produces("text/html;qs=2")
Note that the http "q" values range from 0-1 and the Jersey "qs" values should be >= 1 (1 is default).
(I learned about this from this source, and I wrote a little note for myself here)
As the Jersey User Guide states:
If both are equally acceptable then the former will be chosen because it occurs first.
From what I know, this leaves you with two possibilities/hacks:
Append the file extension to your URIs to override the Accept header
Write a servlet filter that overwrites the Accept header for those User Agents
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With