Platonic URIs

From: Marc Hadley <Marc.Hadley_at_Sun.COM>
Date: Thu, 20 Sep 2007 16:15:38 -0400

Paul has been discussing options for supporting "platonic" URIs in a
couple of recent blog posts[1,2]. In [2] he suggests a URI template
of "/helloworld{media}" to match "helloworld.xml", "helloworld.json"
and "helloworld". This works fine for the example given but runs in a
problem in the more general case of "{id}{media}" since its
impossible to determine where the "id" stops and the "media" starts.
The obvious solution is to use the template "{id}.{media}" which
matches "foo.xml" and "foo.json" but unfortunately it no longer
matches the platonic URI "foo". Paul and I discussed this earlier
today and came up with a few workable approaches:

(i) Use a filter to rewrite requests, this was the solution we seemed
to settle on when this topic came up in April[3]. E.g. a
ServletFilter could take a request for "foo.xml" and rewrite it to be
a request for "foo" with an accept header of "application/xml". This
seems like an OK approach provided the client-supplied accept header
includes the media type that you are overwriting it with. The
downside is that the solution is container dependent, there's no way
to do this using the JAX-RS APIs.

(ii) Use inheritance to effectively associate two UriTemplates with a
resource class. E.g.:

public class HelloWorldResource {...}

public class HelloWorldResource2 extends HelloWorldResource {...}

This would also work but its not an entirely obvious solution so we'd
probably want to highlight it if we recommend this kind of approach.

(iii) We could consider including adding some convention-driven
support to the UriTemplate annotation. E.g.:

@UriTemplate(value="{id}" extentions=true)

The above could surface a fixed-name URI template variable for any
supplied extension so that for "foo.xml" you'd get UriParam("id")
==foo, UriParam("extension")==xml. Alternatively we could go further
and have the runtime do the kind of request rewriting discussed in
(i) so that:

GET /foo.xml
Accept: */*

is internally treated as if it were:

GET /foo
Accept: application/xml

If we went this route we'd need to define a set of default mapping
rules and probably some means to extend or modify the default set.

What do folks think, are (i) and (ii) sufficient or are you
interested in something along the lines of (iii) ? Perhaps there are
other approaches we didn't consider ?



Marc Hadley <marc.hadley at>
CTO Office, Sun Microsystems.