URL query params or media type params (in Accept header) to configure the response to an HTTP request?

14,063

Solution 1

It seems to me that the distinction should run as follows:

Accept header parameters pertain to the packaging of the response.

  • Media type (e.g. application/json)
  • Character encoding (e.g. charset=utf-8)
  • Structure (e.g. vendor extensions that specify the "doctype"; application/vnd.example-com.foo+json; version=1.0)

Query parameters pertain to resource(s) as addressed.

  • Components (e.g. headings and footnotes)
  • Optional features (e.g. formatting)
  • Constraints (especially when addressing a range of like resources)

Solution 2

David Eyk is correct.

If you are changing the Entity returned, it should be in the URI. Anything else will break all sorts of things like caching etc.

The "format" inside the URI however is mostly wrong. If you can, use Accept. Response headers are already in place to provide a smooth ride.

However, if you are unable to use Accept (like in a standard webbrowser) I recommend using a DOS extension or similar to override the Accept.

e.g.
/image (is the resource)
/image.jpg
/image.gif
Share:
14,063
James Tran
Author by

James Tran

Googler 👨🏻‍💻 @AMPhtml, PWA, & open web; @WordPress core team; @xwp alum. Daddy² & husband 👨‍👩‍👦‍👦 Go by 🏃🏻🚴🏻🚌🚈 Philologist & hispanohablante [ˈwɛsˌtn̩ ˈɹuːˌɾɚ]

Updated on June 05, 2022

Comments

  • James Tran
    James Tran almost 2 years

    I'm working on designing a REST API that can respond with a variety of formats, one of which is a plain text format which can be configured to show or hide certain aspects from the response (e.g. section headings or footnotes). The traditional way that this is done is via URL query parameters, both to indicate the desired response type and the configuration options, for example:

    http://api.example.com/foo-book/ch1/?format=text&headings=false&footnotes=true
    

    However, a more elegant RESTful way to indicate the desired response type (instead of the format=text URL query param) is to use the Accept header, for example:

    Accept: text/plain; charset=utf-8
    

    Now, in addition to URLs, media types can take parameters per RFC 2046 and as seen in the ubiquitous text/html; charset=utf-8 and in Accept headers like audio/*; q=0.2. It's also shown that vendor-crafted MIME types can define their own parameters like:

    application/vnd.example-com.foo+json; version=1.0
    application/vnd.example-info.bar+xml; version=2.0
    

    So for previously-registered MIME types like text/html or application/json, is it acceptable to include custom parameters for an application's needs? For example:

    Accept: text/plain; charset=utf-8; headings=false; footnotes=true
    

    This seems like an elegant RESTful solution, but it also seems like it would be violating something. RFC 2046 §1 says:

    Parameters are modifiers of the media subtype, and as such do not
    fundamentally affect the nature of the content.  The set of
    meaningful parameters depends on the media type and subtype.  Most
    parameters are associated with a single specific subtype.  However, a
    given top-level media type may define parameters which are applicable
    to any subtype of that type.  Parameters may be required by their
    defining media type or subtype or they may be optional.  MIME
    implementations must also ignore any parameters whose names they do
    not recognize.
    

    Note this last sentence:

    MIME implementations must also ignore any parameters whose names they do not recognize.
    

    Does this mean that a client would be non-conforming if they recognized a footnotes=true parameter of the text/plain media type?

  • James Tran
    James Tran almost 13 years
    Thanks, Michael. Regarding the format parameter, I realized that this would be the only real option for linking to an alternate format of a resource, per HTML5 example: <link rel=alternate href="/en/pdf" hreflang=en type=application/pdf title="English PDF">. Though perhaps this is kind of format-explicit link is not praiseworthy.
  • James Tran
    James Tran almost 13 years
    What if a query parameter is specific to a particular response type? For example a line-length parameter for text/plain: if a URL exists such as http://api.example.com/foo?line-length=74 and if I request it with Accept: text/html, should the query param simply be ignored? Or should the response contain a Content-Location: http://api.example.com/foo header along with the same URL in a <link rel='canonical' href='http://api.example.com/foo'/> (aka <atom:link rel='self' href='http://api.example.com/foo'/>) to indicate that the parameter wasn't considered when serving the response?
  • fumanchu
    fumanchu almost 13 years
    In theory, you should just be able to add Vary: Accept to the response headers and then nothing would break. But in practice, HTTP implementations can be sketchy on such details.
  • David Eyk
    David Eyk almost 13 years
    That probably belongs in the Accept header, with Vary: Accept being sent as well (thanks, @fumanchu).
  • David Eyk
    David Eyk almost 13 years
    One thing that makes me nervous about a heavy reliance on Accept parameters: a) the level of client support and b) the technical expertise of the people consuming the API. Sometimes RESTful design principles run the danger of committing architecture astronautry at the expense of user experience
  • James Tran
    James Tran almost 13 years
    Agreed. I think that both purer server-driven negotiation should be accompanied by agent-driven negotiation to make accomodations for client limitations, e.g. pulling in data via JSON-P or via PHP's file_get_contents() for which you can't suppled your own request headers.