media types are one of the foundations of web architecture: media types allow clients to understand the rules of engagement
, so that interpretation of state, as well as subsequent interactions, can be driven by that shared understanding. ideally, new services are not reinventing everything and create their complete own set of media types they are using: often they expose a mix of custom media types, reused media types, and some rules of how this web of resources is interconnected by navigable links.
as a little exercise in how such a setup might work and could be made self-describing, let's assume a service uses Atom as a general mechanism for how to represent collections of various kinds, and AtomPub as a general mechanism for how to enable write access to (some of) those collections. this example simply uses parts of the Atom Landscape, but the general pattern in applicable to any scenario where media types are reused, and their usage is refined by additional constraints/extensions.
to make things more interesting, let's also add AtomPatch
, a hypothetical additional protocol that complements AtomPub by adding support for the HTTP PATCH method on collections. let's assume AtomPatch allows you to POST a feed of to-be-patched entries to a collection, either updating entries completely, or using Atom Tombstones to indicate that an entry should be removed (we're actively looking into this as a way to handle bulk updates
to a collection, but that's a different story than the one i am telling here).
the interesting question is how to make all of this as self-describing as possible. when GETting an AtomPub/AtomPatch enabled collection, there is no link in there telling a client that there are additional interaction capabilities to that resource (POST because of AtomPub, and PATCH because of AtomPatch). AtomPub defines the an additional type
parameter for the Atom media type, and because that is defined by AtomPub and not Atom itself, it is assumed that if a URI responds with application/atom+xml;type=feed
, it must be an AtomPub collection and thus clients might want to try POST. but this model breaks now that AtomPatch may or may not be supported: how would a client be able to understand this additional level of information?
this is where the concept of a Profile (explained in this blog post) enters the picture: it allows services to expose the fact that a resource is following one or more profiles. for the example of a AtomPub/AtomPatch-capable collection, profiles could be used in the following way:
<feed xmlns="http://www.w3.org/2005/Atom"> <link rel="profile" href="urn:ietf:rfc:5023"/> <link rel="profile" href="http://example.org/AtomPatch"/> ...
this example uses the AtomPub Profile URI proposed in the Atom Profile draft, and makes up a profile URI for AtomPatch. at its core, though, this draft proposes that application/atom+xml
supports a profile media type parameter, so that the AtomPub-ness
and AtomPatch-ness
could also be exposed on the media type level as follows:
application/atom+xml;profile="urn:ietf:rfc:5023 http://example.org/AtomPatch"
this way, the AtomPub and AtomPatch affordances of the resource are represented both in HTTP's uniform interface (in the media type), and in the representation itself (by exposing the profile URIs in the generic linking element). the interesting twist here is that the interaction affordances of a resource are not represented as navigable links (as defined by link relations based on Web Linking), but instead they are implied because of the capabilities that clients recognizing the profile URI know they can imply.
it is a design decision whether the fact that some resource is extending a media type should be exposed as a profile, or just by adding links to a representation. AtomPatch could also be defined as using a patch
link relation, and exposing this on all PATCHable collections: the protocol would then be to always follow that link with PATCH and the patch format (instead of always PATCHing the collection URI itself).
what profiles allow is a convenient grouping of capabilities: in the same way as a media type often bundles a number of interaction capabilities that probably all should be supported (or at least considered) by clients, a profile does the same for services that are based on existing media types, but constrain and/or extend them. profiles may make it a little easier to make resources self-describing, by referring to an additional context that is optional to know for a client, but may provide additional information about a resource's data model and/or hypermedia affordances.
I like the idea of being able to create specific functionality and affordances by composing it from more elementary pieces.
I might have missed this previously, but how can we negotiate on the supported profiles?
Posted by: Ruben Verborgh | Wednesday, September 11, 2013 at 00:25
The app:collection element can be included in the atom:feed element. In the app:collection element, an empty app:accept element indicates a read-only feed. Media types listed in app:accept can be POSTed. http://tools.ietf.org/html/rfc5023#section-8.3.4.
Posted by: Peter | Wednesday, September 11, 2013 at 06:04
@ruben, i don't think that profiles are quite on the level of media types (for which negotiation is baked into the fabric of the uniform interface). i see where you want to go with this (i think), and at some level you could say that you could negotiate by using Accept and adding a profile media type parameter. or you could go wild and dream up an Accept-Profile header to avoid this "overloading" of Accept. but at some point, things start to get a little complicated... in the end, profiles are just a simple way to expose/encode the "specialization" of a media type, and they are not full-fledged media types by themselves. it actually would be interesting to go through a systematic comparison of where minting new media types, and using profiles, differ, and also, what might be added to the picture (such as the hypothetical Accept-Profile).
Posted by: dret | Thursday, September 12, 2013 at 11:05
@peter, thanks for pointing that out. that's a rather interesting definition of defaults there, saying that 'If no app:accept element is present, clients SHOULD treat this as equivalent to an app:accept element with the content "application/atom+xml;type=entry"'.
this seems to define that every non-AtomPub feed (because it will not have app:accept in it) will always be treated as saying "i am an AtomPub feed" by AtomPub-aware clients. that's one way of doing it, but makes it hard for clients to leave pure Atom feeds alone by understand that they can probably not be POSTed to, if they don't exhibit any signs of AtomPub being supported.
Posted by: dret | Thursday, September 12, 2013 at 11:11
@dret Not quite, because the app:accept element is in the app:collection element which MAY be present in the feed. If you include the app:collection element, you are by definition AtomPub, obviously, but the POSTability then can be determined by the app:accept. The PATCHability may be a good candidate for a profile, however. I think another approach might be an extension of the app:collection control with something relating to PATCH, perhaps based on how the app:accept (POSTability) is modelled. For me, app:collection is the core of a powerful hypermedia control. Thanks for your excellent blog.
Cheers,
Posted by: Peter | Thursday, September 12, 2013 at 11:34
@peter i should have looked closer. i was assuming you can use app:accept on a feed, but you cannot. it can only be used in service documents. but then again, doesn't this mean that my original claim is true that when you just look at a feed, you cannot tell whether it supports AtomPub or not? it may link to a service document, but that's not even supported by AtomPub itself, but only by the "service" link relation which RFC 5899 defined later on.
Posted by: dret | Thursday, September 12, 2013 at 11:44
@dret Thanks, that was indeed where I was going. Especially the decision of when to use a media type and when to use a profile could be an interesting topic. Perhaps Accept-Profile could make media “subtypes” (like +json etc.) obsolete and allow much more fine-grained control.
Posted by: Ruben Verborgh | Thursday, September 12, 2013 at 13:37
@dret Actually, the app:collection element is an optional child element of atom:feed, made so by AtomPub. Of course it is mandatory in the service document (if a collection exists). A typed link to the service document might be enough to tell you it's an AtomPub feed, but as you say that's an extension not defined by the application/atom+xml;type=feed type. If the service *does* decide to include the app:collection element, I think this helps with the HATEOAS constraint, because, by inspecting the *representation*, the client is made aware of next (possible) states. Perhaps having a service document linked, out there is good enough, but I like the explicit inclusion of the hypermedia control *in* the representation. In that case there's no ambiguity then about whether a POST came just out of the blue or was guided/ encouraged by the server.
Cheers,
Peter
Posted by: Peter | Thursday, September 12, 2013 at 13:51
@ruben: as a general rule, you should only use profiles if the representation also can be interpreted in a meaningful way without knowing the profile. a podcast is a profile of a feed (it makes sense to look at a podcast feed without knowing that it is a podcast). but a feed is not a profile of xml, because when you look at feed XML without knowing that it is a feed, all you see is a generic XML tree of elements and attributes: a very different thing altogether.
it is important to keep in mind that profiles try to capture this "you can look at this as a podcast or as a feed" scenario, whereas the "+xml" suffix really just tells you: this representation happens to be serialized in XML. so for example for home documents, we can mint application/home+xml and application/home+json, and it's kind of obvious that these two have something in common. that's where structured suffixes come in.
Posted by: dret | Thursday, September 12, 2013 at 15:31
@peter, i have to admit that i completely forgot about app:collection being allowed as a child of app:feed. and yes, in this case you're right that it's good hypermedia. what's happening there is basically a protocol-specific form of an "allow-post link hint" http://tools.ietf.org/html/draft-nottingham-link-hint#section-3.4 or an Accept-Post HTTP header http://tools.ietf.org/html/draft-wilde-accept-post
and i guess this is what profiles and some other things i have been playing around with come down to: they are approaches of how to create specs that encode and harmonize certain design patterns that we've seen popping up in various places, and in various forms. i am not saying that all of this is the best or the only way to do it, but once you've seen a number of services doing this in a variety of ways, it makes sense to at least think about if and how it could be turned into a standardized and thus easily reusable and understandable component.
Posted by: dret | Thursday, September 12, 2013 at 15:45