Simon Willison
asked for opinions on how to deliver JSON content properly while also assisting browser-driven exploration and debugging.
Here is a short, simple example of how to use content-negotiation to achieve this.
This is a complete, working webmachine resource with trivial content:
-module(conneg_demo_resource).
-export([init/1, to_json/2, content_types_provided/2]).
-include_lib("webmachine/include/webmachine.hrl").
init([]) -> {ok, x}.
to_json(_,X) -> {"{\"key\": \"value\"}\n", X}.
content_types_provided(_,X) ->
{[{"application/json", to_json},{"text/plain", to_json}], X}.
A typical request/response, with a tiny bit of header noise trimmed:
$ curl -v http://localhost:8000/js/simonw
> GET /js/simonw HTTP/1.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Vary: Accept
< Server: MochiWeb/1.1 WebMachine/0.20 (There was kicking.)
< Content-Type: application/json
< Content-Length: 17
<
{"key": "value"}
This is what you generally want. The JSON was delivered with the proper content-type and so on. Note the presence of the "Vary" header.
However, if the same request is made with a typical browser's Accept header, like FireFox's, the result will be different:
> GET /js/simonw HTTP/1.1
> Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
>
< HTTP/1.1 200 OK
< Vary: Accept
< Server: MochiWeb/1.1 WebMachine/0.20 (There was kicking.)
< Content-Type: text/plain
< Content-Length: 17
<
{"key": "value"}
This time we got the same JSON content, but in text/plain so it will display nicely in a browser window if requested directly. A Web application loading this content would probably set the Accept header in XHR requests to "Accept: application/json" and get the first response.
This is a straightforward use of content-negotiation that serves a useful purpose and (by using Accept properly and providing the Vary response header) still works well with intermediaries and the rest of the mechanics of the Web.