Pragmatism, purity and JSON content types
I started a conversation about this on Twitter the other day, but Twitter is a horrible place to have an archived discussion so I’m going to try again here.
If you’re producing a JSON API for other people to use (as opposed to an API that’s only really meant for your own local Ajax responses), you need to decide which Content-Type to use. The best option is not entirely obvious.
RFC 4672 defines JSON and reserves
application/json as the preferred media type. The problem is that most browsers will prompt you to download the file rather than displaying it inline (as they would for
application/json throws a big, frustrating road block in the way. There are ways of telling your browser to treat
application/json in the same way as
text/plain but that doesn’t really help you if your aim is to create an API that’s easy for other developers to use.
It’s also worth mentioning that if you are returning JSONP (with an extra callback function wrapped around the JSON response to enable the dynamic script tag hack) you HAVE to serve as
charset=UTF8 as well (for both types of response).
So, it’s pragmatism v.s. purity. The correct thing to do is to return
application/json, but doing so makes your API harder for developers to use.
In a brief, non-comprehensive review of some existing JSON APIs (FriendFeed, Flickr, Google Social Graph etc) I couldn’t find any that were using
application/json, presumably for this exact reason.
Using the Accept: header
The Accept: header is one of my least favourite parts of HTTP. I like to be confident that if I send a URL to someone, they’ll get back exactly the same bytes as I did when I retrieved it myself (I distrust language negotiation for the same reason). However, a number of people suggested it on Twitter and it looks like it could be a useful solution to this problem.
I’m currently considering the following: ONLY use the
application/json Content-Type in reply to requests that include
application/json in their Accept header—essentially allowing clients that care about the correct content type to opt-in to receiving it. Everyone else (browsers included) gets
A couple of things worry me about this. Firstly, is this a reasonable thing to use Accept for? Secondly, is there a chance that browsers might add
application/json to their Accept header at some point in the future? Safari currently sends
text/xml,application/xml,application/xhtml+xml,text/html; q=0.9,text/plain; q=0.8,image/png,*/*; q=0.5 while Firefox sends
text/html,application/xhtml+xml,application/xml; q=0.9,*/*; q=0.8. Would it be smarter to look for
*/* and serve the incorrect Content-Type to those requests and the correct one to everything else?
An alternative is to simply allow people to specify “JSON with a browsable Content-Type” as an alternative format option, or to enable a “pretty=1” query string argument which returns the response as
text/plain and potentially pretty prints it as well. I haven’t yet decided if this is better than messing around with the Accept header.