Philosophical content negotiation

Arun recently presented an example of how to use JAXB for both XML and JSON representations. In the example Arun uses distinct URIs to access the XML and JSON representations, "/helloworld/xml" and "helloworld/json" respectively.

I thought it would be use to connect things together with a bit of light RESTful philosophy.

Jersey can be used to develop a resource that has one URI to serve XML and JSON representations with content negotiation (see Jakub's example). That URI is referred to as the 'platonic' URI (see my previous blog entries here and here on the subject).

Jersey can also be used to develop a resource that has a platonic URI and distinct URIs. The GET request to the platonic URI will redirect, based on the most acceptable media type, to the distinct URI and a GET request to the distinct URIs can refer back to the platonic URI using the "Content-Location" header.

Below is an example of such a resource that returns XML and JSON representations: 

@UriTemplate("/helloworld")
public class HelloWorldResource {
    static final MediaType XML = new MediaType("application/xml");
    static final MediaType JSON = new MediaType("application/json");
    static final List<MediaType> acceptableList = Arrays.asList(XML, JSON);
   
    @HttpContext
    HttpContextAccess context;

    @HttpContext
    UriInfo uriInfo;
   
    @HttpMethod
    @ProduceMime({"application/xml", "application/json"})
    public Response get() {
        MediaType contentType = context.getHttpRequestContext().
                getAcceptableMediaType(acceptableList);

        UriBuilder ub = uriInfo.getBuilder();
        if (contentType.equals(XML)) {
            ub.path("xml");
        } else {
            ub.path("json");
        }
        URI distinctUri = ub.build();
       
        return Response.Builder.temporaryRedirect(distinctUri).build();
    }

    @HttpMethod
    @ProduceMime("application/xml")
    @UriTemplate("/xml")
    public Response getXML() {
        URI platonicUri = uriInfo.getBaseBuilder().path(this.getClass()).
                build();
               
        return Response.Builder.
                representation("<content>distinct XML</content>").
                contentLocation(platonicUri).
                build();
    }

    @HttpMethod
    @ProduceMime("application/json")
    @UriTemplate("/json")
    public Response getJSON() {
        URI platonicUri = uriInfo.getBaseBuilder().path(this.getClass()).
                build();
               
        return Response.Builder.
                representation("{'content': 'distinct JSON'}").
                contentLocation(platonicUri).
                build();
    }
}

This highlights nicely the building of URIs and responses. I deliberately wrote one method to support the GET request to the platonic URI for testing out the API. I think it shows it requires some improvement as such information could be easily calculated and returned by the runtime without having to create a list of media type. However, one could always do the following instead:

    @HttpMethod 
    @ProduceMime("application/xml")
    public Response getPlatonicXML() {
        URI distinctUri = uriInfo.getBuilder().path("xml").build();
       
        return Response.Builder.temporaryRedirect(distinctUri).build();
    }
   
    @HttpMethod
    @ProduceMime("application/json")
    public Response getPlatonicJSON() {
        URI distinctUri = uriInfo.getBuilder().path("json").build();
       
        return Response.Builder.temporaryRedirect(distinctUri).build();
    }

My guess is it should be possible to support a platonic mapping to such methods automatically by the runtime analyzing the patterns of HTTP methods declared on the Java class. That way developers do not have to explicitly support such functionality.

Comments:

Lots of duplicated code there. I think ideally, if you can support either of xml or json, you should allow an affordance of one URL that returns whatever the user asked for. All those redirects are going to be expensive.

Usually when I think of the URL doing the content negotiation, I tend to think of handling this by looking for the content type as a file extension. ie, helloWorld.xml or helloWorld.json. This has the nice bene that if someone uses curl to access your urls, the default file name will actually make sense as a local file as well. And it would be lovely to be able to have one @HttpMethod that could handle this distinction as well, instead of two.

Posted by Patrick Mueller on September 19, 2007 at 01:36 PM CEST #

Point taken about the code duplication, apart from showing the use of the URI and Response builders i hope such examples can help provide input into improving support for platonic and distinct URIs, using redirection or not. If you have any ideas on this i would be very interested, please send them to the Jersey users list.

There is another way of doing this which i will blog about. However, in general i am not sure yet whether it is a good thing to hide a set of URIs under one thing without the developer explicitly requesting such behaviour so that they are aware of the consequences.

W.r.t. redirection i was looking at "Cool URIs for the Semantic Web" [1], which is being discussed in the W3 tag.

[1] https://gnowsis.opendfki.de/repos/gnowsis/papers/2006_11_concepturi/html/cooluris_sweo_note.html

Posted by Paul Sandoz on September 20, 2007 at 05:00 AM CEST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

sandoz

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today