RESTful Profiling : XHTML-based Web APIs
NOTE
this is another post in my ongoing series about hypermedia, and Web API design.
When designing your hypermedia Web API, you don't always have to use a custom media-type to express your data and linking semantics. Instead you can use a well-known, registered media-type. the advantage of registered media-types is that they are more likely to be understood and rendered properly by common Web browsers and other existing generic clients. the disadvantage of using registered types is that they usually lack the specific semantics you need for your service or application. that means you need to extend the registered type in a way that clients can understand. this process of extending is usually handled differently for each media-type.
XHTML as your Hypermedia API media-type
XHTML
is a very powerful media-type and can be easily extended to include your specific data and link semantics.
XHTML's link elements (A
tags) support the REL
attribute and FORM
elements support the
NAME
attribute. these can attributes be used to mark the application flow semantics for your Web API.
you can provide query and write templates using the FORM
and INPUT
elements.
as is the case for most all general media types, some compromises need to be made when you adopt the type for your
specific needs. for example, XHTML only defines POST
and GET
as valid values for the
METHOD
attribute of FORM
elements. while HTML5 will soon support DELETE
and PUT
values for the METHOD
attribute, current implementations using X/HTML will need to deal with this limitation by either sending
additional code-on-demand
to common browsers (my preferred approach) or re-design the client-server interactions to support only
POST
and GET
. note that for cases wherer your Web API is used only with custom-built clients
(bots, desktop applications, RIAs, etc.), this METHOD
attribute value limitation likely can be ignored.
List Service Redux
using the same general web service i outlined in a previous post, here's one way to implement the List Service using XHTML as the hypermedia type:
<?xml version="1.0" encoding="utf-8"?> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title>List Service</title> </head> <body> <div class="list"> <!-- list collection --> <dl> <dt> <a href="{item-uri}" rel="item" title="item">{item-uri}</a> </dt> <dd> <span class="title">First Task</span> <span class="description">Produce first draft of Task media-type</span> <span class="date-due">2010-04-21</span> <span class="completed">false</span> </dd> <dt> <a href="{item-uri}" rel="item" title="item">{item-uri}</a> </dt> <dd> <span class="title">Second Task</span> <span class="description">Implement REST version of Task Service over HTTP</span> <span class="date-due">2010-04-23</span> <span class="completed">false</span> </dd> <dt> <a href="{item-uri}" rel="item" title="item">{item-uri}</a> </dt> <dd> <span class="title">Next</span> <span class="description">Go hiking</span> <span class="date-due">2010-04-23</span> <span class="completed">false</span> </dd> </dl> <!-- adding a new item --> <form name="add" method="post" action="{collection-uri}"> <input type="text" name="title" /> <input type="text" name="description" /> <input type="text" name="date-due" /> <input type="text" name="completed" /> <input type="submit" value="add" /> </form> <!-- item update & delete --> <form name="edit" method="put" action="{item-uri}"> <input type="text" name="title" /> <input type="text" name="description" /> <input type="text" name="date-due" /> <input type="text" name="completed" /> <input type="submit" value="edit" /> </form> <form name="delete" method="delete" action="{item-uri}"> <input type="submit" value="delete" /> </form> <!-- queries --> <form name="today" method="get" action="{today-query-uri}"> <input type="submit" value="today" /> </form> <form name="open" method="get" action="{open-query-uri}"> <input type="submit" value="open" /> </form> <form name="date-range" method="get" action="{date-range-query-uri}"> <input type="text" name="date-start" /> <input type="text" name="date-stop" /> <input type="submit" value="date-range" /> </form> </div> </body> </html>
you'll note that the XML elements for the list that appeared in my custom media type example now appear as XHTML's
DL
, DT
, and DD
elements. the individual data elements within an item are expressed as
SPAN
s. note also that i've used the CLASS
attribute to provide the
"meaning" of each of the important data elements. this is similar to the
Microformat approach to adding semantic meaning to HTML content.
the query and write operations are expressed using FORM
elements and INPUT
elements where appropriate.
i use DELETE
and PUT
as values for the METHOD
attribute here even though i know that
common browsers that don't support HTML5 will ignore these values (actually convert them to GET
). i can handle
this limitation using code-on-demand for javascript, if necessary.
this rounds out the extension of XHTML to handle the semantic requirements of my simple List Service. all that's left is to make sure both clients and servers recognize and understand these semantic extentions.
but how do i *know* what you mean?
when using extended media types, it is not easy for clients or servers to know (ahead of time) the nature of those additional
data and link semantics. custom media-types carry this information in the media-type name and the associated documentation.
using a general media-type makes expressing the semantics more difficult. one possibility is to modify the media-type name
itself (text/html;type=list-manager
, but this has problems, too.
luckily, XHTML already has a very handy way to convey the semantics of the document: the PROFILE
attribute.
using XHTML Profiles to express media semantics
HTML defines the PROFILE
attribute
for HEAD
and META
elements as a way to include additional information about the contents of the HTML document.
you can use this attribute as a way to indicate the semantic details of the response. IOW, the profile can act in the same way a custom
media-type name does. here's an example:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head profile="http://www.example.org/profiles/list-manager" > <title>List Service</title> </head> ... ** OR ** <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title>List Service</title> <meta name="profile" href="http://www.example.org/profiles/list-manager" /> </head> ...
in cases where the HTTP protocol is used, this profile URI can be added as control data using a Link Header:
REQUEST *** GET /lists/my-list HTTP/1.1 Host: www.example.org Accept: application/xhtml+xml RESPONSE *** HTTP/1.1 200 OK Link: <http://www.example.org/profiles/list-manager>;rel="profile" Content-Type: application/xhtml+xml Content-Length: xxx ...
when handling the response, clients can inspect the PROFILE
attribute (or link header) to confirm the semantics of the
content. if the client does not recognize the profile, it can choose to ignore it (as the common browser does) or reject the document
as unknown (the same way browsers act when they see a media-type they do not understand).
as is indicated in the spec, the profile attribute can be treated as a reference URI (a simple id) or as a resolvable URI that points to another document with details on the profile. i use the second approach and place a resource at the other end of the URI that documents the semantic details in a way similar to that of a registered custom media type. i use Tantek Çelik's very nice example of how to document HTML profiles.
bottom line
if you do not want to create your own custom media-type, you can adopt an existing, registered type and extend it with your service's
specific data and link semantics. for me, XHTML is the best choice for an "extensible" hypermedia type. it contains most of the
link elements a Web API needs (A
, FORM
, and INPUT
) and, if you adopt the Microformat model
for markup up your XHTML-based Web APIs, you can probably the same responses for both common browsers and targeted clients
(bots, desktop apps, RIAs, etc.).
btw, are you wondering why i did not select Atom as my "go-to" registered media-type for Web API design? maybe i'll cover that next week.