前回は REST で受け渡しされるリソース同士が、 URI を使ったハイパーリンクで相互に結び付けられることによってハイパーメディアを実現し、 その機能拡張には XML 名前空間などが利用できることを説明しました。
今回はより応用的なハイパーメディアと REST の関係を見ていきたいと思います。 例によってはてなブックマークを使って説明します。
リソースの URI はひとつではない
現状のはてなブックマークでは、 ひとつのブックマークエントリ(リソース)の URI は僕が知る限り以下の三つがあります。
- ブックマーク一覧
- http://b.hatena.ne.jp/yohei/20050429#187800
- 人間用 Edit URI
- http://b.hatena.ne.jp/yohei/edit?eid=187800
- Atom API 用 Edit URI
- http://b.hatena.ne.jp/atom/edit/187800
このうち、一覧の URI は少し毛色が違いますが、 人間用および Atom API 用 EditURI は同じリソースを指していると考えて問題ないでしょう。 このようにひとつのリソースが URI を複数持つというのは珍しいことではありません。
同じリソースの別の URI にリンクしてみる
ただ、現状では人間用と Atom API 用がお互いに無関係なのが少しさみしいですね。 以下のようにお互いの表現(representation)がリンクしていたらどうでしょうか。
人間用 http://b.hatena.ne.jp/yohei/edit?eid=187800
<html>
<head>
<title>はてなブックマーク - 傭兵ブックマーク</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="/yohei/style" />
<link rel="service.edit" type="application/atom+xml"
title="ブックマークの編集"
href="http://b.hatena.ne.jp/atom/edit/187800" />
</head>
<body>
...
</body>
</html>
Atom API 用 http://b.hatena.ne.jp/atom/edit/187800
<entry xmlns="http://purl.org/atom/ns#">
<title>何はなくとも XML InfoSet: アウトプットの定義</title>
<link rel="related" type="text/html"
href="http://luckypines.blogspot.com/2005/04/blog-post_28.html" />
<link rel="alternate" type="text/html"
href="http://b.hatena.ne.jp/yohei/20050429#187800" />
<link rel="alternate" type="text/html"
href="http://b.hatena.ne.jp/yohei/edit?eid=187800" />
<link rel="service.edit" type="application/x.atom+xml"
href="http://b.hatena.ne.jp/atom/edit/187800"
title="何はなくとも XML InfoSet: アウトプットの定義" />
<author>
<name>yohei</name>
</author>
<generator url="http://b.hatena.ne.jp/" version="0.1"
>Hatena::Bookmark</generator>
<issued>2005-04-29T20:00:05+09:00
<id>tag:hatena.ne.jp,2005:bookmark-yohei-187800</id>
<summary type="text/plain"
>一連の議論の中で一番共感しました。脳みそへのインターフェース</summary>
</entry>
URI の可搬性
リンクするとうれしいことはいろいろあると思うのですが、 たとえば僕はこんなことを考えました。
はてなブックマークの専用クライアントがあるとします。 ブックマークの編集操作をしたり、ブックマークエントリを他の blog にポストしたり、別のソーシャルブックマークサービスにコピーしたりする機能を持っています。 ただ、そのままだと専用クライアントは通常あまり使いません。 なぜかというと Web を閲覧するのは Web ブラウザでするし、 ブックマークするのも bookmarklet でできるからです。 専用クライアントを使うのはせいぜいブックマークの大量削除などのときくらいでしょう。
しかし、たとえばこんな使い方ができたらどうでしょうか。 ブラウザと同じように、専用クライアントも URI 欄(アドレス欄)を持っているとします。 ブラウザで表示しているはてなブックマークの URI をコピーして専用クライアントに貼り付けられます。 すると、その URI で特定されるブックマークエントリに対して、 その専用クライアントで可能な操作が一覧される。 あるいはあらかじめ操作を選んでおいて、 URI を貼り付けるとはてなブックマークのエントリが del.icio.us と spurl に自動的にコピーされる。
ブックマークエントリリソースの表現が相互にリンクされていれば、 このプログラムの実装は簡単です。 専用クライアントは渡された URI を HTTP GET します。 返ってくる内容の Content-Type が text/html であれば、 /html/head/link[@rel="service.edit"]/@href を検索し(こんなの XPath で一発ですね) EditURI を GET すればプログラムで扱いやすい XML 形式のリソースの表現が取得できるからです。 あとは、得られた情報を適切に操作するだけです。
URI のコピーアンドペーストは最も原始的なリンクだといえます。 手作業でのコピペはダサいんですが、 これを自動化するのは簡単でしょう。 専用クライアントがブラウザの拡張プログラムであれば、 リンク選択時の右クリックメニューに「専用プログラムへ渡す」 という項目を追加するだけです。 この機能はハイパーリンクに他なりません。
このように、 URI だけでアプリケーション連携を実現できるのが REST(WWW) アーキテクチャスタイルの素晴らしい点です。 これはいわゆる疎結合(loosely-coupled)という性質です。
コンテントネゴシエーションによるソリューション
前節の URI コピペの実装には別の方法もあります。 HTTP のコンテントネゴシエーションを使う方法です。 これは同じ URI でも、クライアントが指定した Content-Type のリソース表現を返すというものです。
例を見てみましょう。 専用クライアントは、人間用 URI に以下のリクエストを発行します。
GET /yohei/edit?eid=187800 HTTP/1.1 Host: b.hatena.ne.jp Accept: application/x.atom+xml; q=1.0, text/html; q=0.8, */*; q=0.1
サーバは Accept ヘッダを解析することで、 クライアントが必要としているのが HTML よりも Atom 形式であることがわかります。
このように実装してあると、 ブラウザで表示している URI と専用クライアントが操作する URI がまったく同じになります。 ただし、クライアント側の選択で、転送されるリソースの表現が HTML になったり、Atom になったりします。
XHTML によるソリューション
実はさらに別の方法も考えられるのです。 それはブックマークリソースが返す表現を XHTML にして、 head 要素の中に Atom の XML を丸ごと入れてしまう方法です。 このようにすれば、そもそも表現自体をクライアントごとに分ける必要もなく、 ただひとつの URI で済ませることができるでしょう。
まとめと課題
これまで見てきたように、URI と HTTP を組み合わせたハイパーメディアにはいろいろな可能性がありそうです。 さらに XML/XHTML を組み合われば、 ブラウザとそれ以外の専用アプリ(あるいはブラウザの拡張)が 自由に連携できそうです。
しかし、現実はそれほど甘くありません。 きちんと REST アーキテクチャスタイルの制約に従ったアーキテクチャの Web アプリケーションであれば、 あまり問題なく XML を使った RESTful な Web サービスを提供できるでしょう。 しかし世の中にはそうでない Web アプリケーション実装が多いのも事実です。
次回は REST アーキテクチャスタイルに反する Web の実装を見ていこうと思います(連休中に書ききれるかと思ったけど無理っぽいなー)。
「同じリソースの別の URI にリンクしてみる」の最初の例の赤字強調部分はrel="service.edit"の方の間違いでしょうか。
返信削除近藤さん
返信削除ご指摘ありがとうございます。そのとおりです。修正しました。