Just recently I realized that the Jersey JAX-RS implemention has a micro-MVC framework built into it. This enables us to send a "Viewable" response (e g a jsp-page) based on content negotiation. Some obstacles got in my way, but the end result was fine (I think).
My initial approach (not working) was just something simple like:
If you send requests to something like http://domain/myresource the form of response will typically depend on the HTTP Accept header. This is essentially what is termed "Content negotiation".
You would expect a JSON response if you send "application/json" as Accept header (maybe some REST api client through an xhr request), or an html response if you point your browser towards the same url. At least i thought so, naively assuming browsers send reasonable Accept headers. It turns out that they don´t.
The HTTP specification states that the user or user-agent can express a "relative degree of preference" using a parameter "q" with values ranging from 0-1 (1 = default/highest degree of preference).
The accept header looks like the following for different browsers:
FF4:
text/html, application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Ok. Nice, this will work out fine.
IE 9:
text/html, application/xhtml+xml, */*
Ok, this is nice too.
IE 7/8 :
image/gif, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/x-shockwave-flash, */*
WTF, pretty much anything you could think except "text/html " is explicitly accepted. Ok, if its all we can serve, sure "*/*" will allow it, but in our case then we might as well get json instead.
application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
WTF2: This is not funny either. This means Webkit browsers will prefer an xml response over text/html. I read somewhere that the reason was that the header was simply copied from what Firefox was using at the time, so older versions of firefox (v <= 2.0) should behave like this too.
Luckily Jersey has a way to overcome this and override the "q" values from the Accept header. This is done by adding a "qs" (= quality of service) parameter to the @Produces annotation that you want to take precedence. Note #1: Only the "q" value is ignored, the media type must still be an acceptable one. Note #2 that this parameter should have values >= 1 (1 = default) , whereas the "q" ranges from 0 - 1.
The modified trivial implementation looks like this (only change is line #4):
Luckily Jersey has a way to overcome this and override the "q" values from the Accept header. This is done by adding a "qs" (= quality of service) parameter to the @Produces annotation that you want to take precedence. Note #1: Only the "q" value is ignored, the media type must still be an acceptable one. Note #2 that this parameter should have values >= 1 (1 = default) , whereas the "q" ranges from 0 - 1.
The modified trivial implementation looks like this (only change is line #4):