1 of 1 found this page helpful.

Designing URIs

Last modified 13:15, 23 Dec 2008

This page captures thoughts and ideas on how to design URIs for WOA/RESTful web-services and applications.  Technically, URI design is irrelevant as all URIs should be discovered through request-response exchanges between the user agent and the server.  However, in practice, well designed URIs have been conducive to good architecture.

Example 1: URIs for versioned multilingual documents

Problem statement: imagine having a model where you have a document identified by an docId that can have different translations (one of them being the default) and revisions (each translation has its own independent revision history).

Approach 1: using URI segments

One could model these resources in the following way.

Path
Meaning
/{docId} current version in default translation
/{docId}/versions list of versions in default translation (Q: should this redirect to /{docId}/translations/{lang}/versions using the default language?)
/{docId}/versions/{version} specific version for default translation (NOTE: this makes no sense since each translation has its own version list)
/{docId}/translations list of translations
/{docId}/translations/{lang} current version for a given translation
/{docId}/translations/{lang}/versions list of versions for a given translation
/{docId}/translations/{lang}/versions/{version} specific version for a given translation
Benefits
  • Usage is fairly straightforward.
Drawbacks
  • URIs are longer than for other approaches.
  • Has one degenerate case where a version of a document can be requested without setting the preferred translation first.
  • No mechanism for URI discovery, meaning the user agent has hard-coded knowledge of URI structure.
  • No mechanism for user agent to specify alternates if requested translation does not exist.  Instead the alternate translation appears to be determined by the server.

Approach 2: Using query parameters

Or, with the same expressive power, one might do:

Path
Meaning
/{docId} current version in default translation
/{docId}/versions list of versions in default translation (Q: should this redirect to /{docId}/versions?translation={default} using the default language?)
/{docId}?version={v} specific version for default translation (NOTE: this makes no sense since each translation has its own version list)
/{docId}/translations list of translations
/{docId}?translation={lang} current version for a given translation
/{docId}/versions?translation={lang} list of versions for a given translation
/{docId}?translation={lang}&version={v} specific version for a given translation
Benefits
  • URIs are shorter than Approach 1.
Drawbacks
  • Has one degenerate case where a version of a document can be requested without setting the preferred translation first.
  • No mechanism for URI discovery, meaning the user agent has hard-coded knowledge of URI structure.
  • No mechanism for user agent to specify alternates if requested translation does not exist.  Instead the alternate translation appears to be determined by the server.

Approach 3: Using Accept-Language header and query parameters

The Accept-Language header can be used by a user agent to request resources in various language and even provide hints at suitable alternatives when the requested language is not available.

Path
Meaning
/{docId} current version of preferred translation
/{docId}/versions list of versions in preferred translation
/{docId}?version={v} specific version for preferred translation
/{docId}/translations list of translations

For user agents that cannot provide a custom HTTP Accept-Language request header, the API could define an override query parameter (e.g. ?accept-language=...).

Benefits
  • URIs are shorter than Approach 1 & 2.
  • Leverages the built-in HTTP mechanism for negotiating the preferred translation with a user agent.
  • No degenerate case since server relies on the presence of the Accept-Language request header.
Drawbacks
  • No mechanism for URI discovery, meaning the user agent has hard-coded knowledge of URI structure.
  • Responses must include Vary response header to indicate proper caching behavior.

Addendum 1: URI discoverability

All above approaches fail to address one important aspect: URI discoverability.  Without the means for the user agent to "discover" the various URIs that can be used to interact with the document, the user agent must hard-code the URI design.  Such hard-coding prevents future changes the URI design or requires the server administrator to manage redirect from old designs to the latest design.

The solution is to return a hypermedia or XML document instead of the contents of the document for path /{docId}.  The returned document provides descriptions for the various URIs that a document can be requested by.

Path
Meaning
/{docId} hypermedia or XML document describing the resource

Let's assume for this example that the returned response is an XML document.  It may look as follows:

<doc translation="en">
  <link rel="contents">http://...</link>
  <link rel="versions">http://...</link>
  <link rel="translations">http://...</link>
</doc>
Benefits
  • Server remains in control of the URI design.
  • User agent is completly decoupled from server design up to the shared semantics of the hypermedia/XML document used to discover the other links.
Drawbacks
  • An additional request-response exchange is required to go from the /{docId} to the contents of the document.

Note: the request-response exchange could be avoided by defining a custom MIME type (e.g. application/x.doc-meta+xml) and using the Accept request header, where the default behavior for /{docId} would be to return the current verion of the translation specified by the Accept-Language header.

Was this article helpful?
Pages that link here
Page statistics
16249 view(s) and 20 edit(s)
Social share
Share this page?

Tags

This page has no custom tags set.

Comments

You must to post a comment.

Attachments