fredag 1 april 2011

Overcoming browsers´ strange media type preferences and creating viewable responses in Jersey

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.

Chrome and Safari (I guess all Webkit browsers):
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):

torsdag 31 mars 2011

Not quite a blog

I figure this will be more like notes to myself, therefore I will write mostly in swedish. If I stumble upon something way cool, or figure out something otherwise poorly documented, I may try the english language for googleability,