agent-driven conneg in HTTP
one of the cool aspects of the HTTP application protocol is that it is "representation-based." the protocol has the built-in ability to provide multiple representation formats for the same requested resource (e.g. HTML, JSON, CSV, etc.) and allow clients and servers to "negotiate" the selection of the representation. recently, i've run into a couple questions regarding one form of that negotiation called Agent-Driven Negotiation . in agent-driven conneg, the server sends the client a list of available representations of the requested resource and the client application selects the one to view. this is very different from the more common Server-Driven Negotiation where the client sends the server a list of preferred content-types (via the Accept header) and the server decides which representation to send to the client.
why use agent-driven conneg?
usually we want representation selection to happen behind the scenes; magically. IOW, we want the server to find a "best match" representation format for the client's needs and send it along. This method works well when the client has a clear preference (e.g. Web browsers prefer HTML representations, spreadsheet applications prefer CSV, etc.). however, some content negotation scenarios make it difficult for the server to make the "right" choice. for example, a server might have several ways to represent statistical data (a graph image, a multi-column table, or plain text data list). Typically, this is not a choice easily handled using server-driven negotation. more often, this is the kind of choice we expect clients (agents) to make once they know what is available.
to the spec!
the HTTP spec is clear on how to handle agent-driven conneg:
With agent-driven negotiation, selection of the best representation for a response is performed by the user agent after receiving an initial response from the origin server. Selection is based on a list of the available representations of the response included within the header fields or entity-body of the initial response, with each representation identified by its own URI. Selection from among the representations may be performed automatically (if the user agent is capable of doing so) or manually by the user selecting from a generated (possibly hypertext) menu.
agent-driven conneg for humans
essentially, for agent-driven conneg, servers provide an "interim response" to client requests. this response includes a list of the available representations of the requested resource and allows the client to make the selection. the spec indicates that servers should return a "300 Multiple Choices" status code; it's not appropriate to use a "200 OK" status code for this list of available representations since the list is, in effect, part of a redirection sequence.
for example, let's assume there is a resource (
/data) that contains a link another resource
/results) that represents results from some statistical calculations. here's the first request/response
pair to pull the
*** REQUEST GET /data HTTP/1.1 Host: www.example.org Accept: text/html *** RESPONSE HTTP/1.1 200 OK Host: www.example.org Content-type: text/html Content-Length: xxx ... <a href="/results">Results</a> ...
next, let's assume there are multiple representations available for the results resource. using agent-driven conneg, here is one possible solution where HTML is the representation format. this will allow the person driving the client application to see a list of possible representations and pick the one that is preferred.
*** REQUEST GET /results HTTP/1.1 Host: www.example.org Accept: */* *** RESPONSE HTTP/1.1 300 Multiple Choices Host: www.example.org Content-Length: XXX <p> Select one: </p> <a href="/results/chart">Pie chart</a> <a href="/results/table">Data Table</a> <a href="/results/list">Text List</a>
automated agent-driven conneg
there may be times when you want to allow the client application (not the human driving it) to make the selection based on the "300 Multiple Choices" response. that means you'll need to provide additional information in the response that the client application can understand. for example, a client may have a preference setting for exporting data to disk formats. this setting could be used to select from a list of possible representations.
*** REQUEST GET /results HTTP/1.1 Host: www.example.org Accept: */* *** RESPONSE HTTP/1.1 300 Multiple Choices Host: www.example.org Content-Length: XXX <p> Select one: </p> <a href="/results/pdf" type="application/pdf">PDF Export</a> <a href="/results/html" type="text/html">HTML Document</a> <a href="/results/csv" type="text/csv">Spreadsheet Export</a>
now the client application can do the same work servers would do when implementing Server-Driven Negotiation . the client can compare the available representations against the client's list of preferred export formats and perform the "best match" selection itself.
understanding the language
the process of negotitating for a representation relies on more than just a data format selection; it might require a language selection, too. Below is an example using the HTML anchor tag's hreflang attribute:
*** REQUEST GET /results HTTP/1.1 Host: www.example.org Accept: */* *** RESPONSE HTTP/1.1 300 Multiple Choices Host: www.example.org Content-Length: XXX <p> Select one: </p> <a href="/results/fr" hreflang="fr">French</a> <a href="/results/en-US" hreflang="en-US">US English</a> <a href="/results/de" hreflang="de">German</a>
letting it all go to your head
there may be a case where returning an HTML representation body for a "300 Multiple Choices" response is not appropriate. for example, a server might have several different image formats for the same resource. in that case the response might use the new Web Linking spec (RFC5988) to provide the selection information as link headers:
for example, if the initial request were rendered in HTML not as an A tag, but as an IMG tag, the interaction might look like this:
*** REQUEST GET /data HTTP/1.1 Host: www.example.org Accept: text/html *** RESPONSE HTTP/1.1 200 OK Host: www.example.org Content-type: text/html Content-Length: xxx ... <img src="/results" /> ... *** REQUEST GET /results HTTP/1.1 Host: www.example.org Accept: image/* *** RESPONSE HTTP/1.1 300 Multiple Choices Host: www.example.org Content-Length: 0 Link: <http://www.example.org/results/png>; type="image/png", <http://www.example.org/results/jpeg>;type="image/jpeg", <http://www.example.org/results/gif>;type="image/gif"
it should be pointed out that, in these last two examples of "automated client selection", client applications can be coded to treat "300 Multiple Choices" similar to other HTTP redirect status codes (301, 302, etc.). the client can make the selection and fire off the redirected request w/o the need for human intervention. like the other redirect status codes, human users may never know they have experienced this client-side negotation and redirection.
alternates vs. conneg
some folks might look at the contents of these interim responses and conclude that they could just as easily be provided within the initial response. that would be true. for example, there might be a resource that lists all the possible alternate views of the data. this resource might contain a set of links to each available representation:
*** REQUEST GET /results HTTP/1.1 Host: www.example.org Accept: text/html *** RESPONSE HTTP/1.1 200 OK Host: www.example.org Content-Length: XXX <p> Select one: </p> <a href="/results/chart" rel="alternate">Pie chart</a> <a href="/results/table" rel="alternate">Data Table</a> <a href="/results/list" rel="alternate">Text List</a>
note that, since this an actual resource, the response status code is set
to "200 OK" instead of "300 Multiple Choices." effectively, this redefines the resource at the URI
from one that represents the statistical results to one that represents a list of available representations
of the statistical results (did you follow that one[grin]?).
but don't get confused
as a thread from 2009 on the Public HTML list shows, more than one person has thought about combining the power of listing alternates in the initial response w/ the flexibility of allowing clients to automatically negotiate for their preferred representation based on content-types. for example, you might think this is a possible solution:
*** REQUEST GET /results HTTP/1.1 Host: www.example.org Accept: text/html *** RESPONSE HTTP/1.1 200 OK Host: www.example.org Content-Length: XXX <p> Select one: </p> <a href="/results" rel="alternate" type="image/png">Pie chart</a> <a href="/results" rel="alternate" type="text/html">Data Table</a> <a href="/results" rel="alternate" type="text/plain">Text List</a>
this approach results in clients receiving a list of alternate representations all w/ the same URI
/results) and expects the client to inspect the
type attribute to select the preferred representation.
however, this solution confuses the two response types covered in this post (
300 Multiple Choices and
there is no need to ask clients to scan the bodies of
200 OK responses in order to determine which alternate
representation format should be requested.
also, rendering a fixed list of representations (tied to specific content-types) in the primary resource may not be a good decision over time. Roy Fielding's response on the above-referenced discussion thread puts it this way (partial quote):
The purpose of conneg is to remove such explicit type indications from the distributed content (HTML) so that such coupling of standards would not be embedded in the web for eternity.
it's all about commitment
so what's the difference between using type="..." w/ 300 Multiple Choices and using rel="alternate" w/ 200 OK? is there an advantage of one or the other? the only difference|advantage is in the commitment the server wants to make about the possible alternate representations of a resource.
The "300" approach is designed to allow servers to treat the list of possible representations as transient and off-load the work of selecting the appropriate representation to the client. in addition, the spec provides an option for the client to do the selection automatically. this approach keeps any changes in the available representations out of the primary resource so that, as formats and availability change over time, the primary resource never needs to change. even cached copies will never be "hard-coded" to a fixed set of possible representation options.
the "200" approach is designed to allow primary resources to provide a fixed set of possible representation. this means clients will always know what representation formats are available. cached copies of this resource will always show the same set of (in the case of my initial example, three) alternate representations even if one or more of those options have changed over time or is no longer available.
these implementation options offer resource designers the ability to choose between flexibility ("300") and specificity ("200"). and choice is what it's all about.
extra credit work
this post covered Agent-Driven Negotitation and referenced Server-Driven Negotation. if you really want to get a handle on how HTTP representation negotation options, check out Transparent Negotitation and work up a scenario where this is a viable option.