RESTful Profiling : XHTML-based Web APIs

2010-04-19 @ 13:59#
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 SPANs. 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.

REST