前回、はてなブックマークを例にリソースと四つの HTTP メソッド (GET, POST, PUT, DELETE) の関係について解説しました。 今回は、リソース同士をどのように関連付けるか、について解説します。
前回の記事でこんな疑問を持った人はいなかったでしょうか。
ひとつのブックマークリソースにいろいろなメソッドを適用しているけれど、 そのブックマークリソースにはいったいどうやってたどり着くのか。
たとえば PostURI に POST して新規作成したブックマークを全部覚えておけば、 それぞれのリソースの URI はわかります。 しかしそれではソーシャルブックマークサービスを使う意味がありません。 ローカルディスクまたはローカルサーバに保存しておくのではブックマークを共有することができないし、 そもそも自分以外の人のブックマークにアクセスできないのでは、 ソーシャルブックマークとは言えません。 残念ながら、現状のはてなブックマーク AtomAPI は、以下の操作しか提供されていません。
- 新規ブックマークの投稿 (PostURI への POST)
- 投稿したブックマークのタイトル、コメントの変更 (EditURI への PUT)
- 投稿したブックマークの削除 (EditURI への DELETE)
- 投稿したブックマークの参照 (EditURI への GET)
- 最近投稿したブックマークの一覧の取得 (FeedURI への GET)
他のユーザのブックマークを閲覧することもできないし、 そもそも自分が登録したブックマークの一覧を全部取得することもできません。
はてなブックマーク AtomAPI はまだ発展途上のサービスなので、機能不足なのは仕方ありませんが、 REST とハイパーメディアの関係を語るにはちょっとさみしいのも事実です。 以下では、はてなブックマーク AtomAPI にない機能を独自に拡張しながら、 REST とハイパーメディア、それから XML の関係を解説します。
リソースの関連付け -- ハイパーリンク
REST ではリソース同士の関係をハイパーリンクで記述します。 たとえば、自分が「最近登録したブックマークのリスト」というリソースの URI は
http://b.hatena.ne.jp/atom/feed
になります。 この URI を GET してみましょう。
GET /atom/feed HTTP/1.1 Host: b.hatena.ne.jp X-WSSE: UsernameToken Username="yohei",...
HTTP/1.1 200 OK Content-Type: application/x.atom+xml <feed version="0.3" xmlns="http://purl.org/atom/ns#" xml:lang="ja"> <title>傭兵ブックマーク</title> <link rel="alternate" type="text/html" href="http://b.hatena.ne.jp/yohei/" /> <link rel="service.post" type="application/x.atom+xml" href="http://b.hatena.ne.jp/atom/post" title="傭兵ブックマーク" /> <modified>2005-04-29T21:43:51+09:00</modified> <author> <name>yohei</name> </author> <id>tag:hatena.ne.jp,2005:bookmark-yohei</id> <generator url="http://b.hatena.ne.jp/" version="0.1">Hatena::Bookmark</generator> <entry> <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="service.edit" type="application/x.atom+xml" href="http://b.hatena.ne.jp/atom/edit/187800" title="何はなくとも XML InfoSet: アウトプットの定義" /> <issued>2005-04-29T20:00:05+09:00 <author> <name>yohei</name> </author> <id>tag:hatena.ne.jp,2005:bookmark-yohei-187800</id> <summary type="text/plain" >一連の議論の中で一番共感しました。脳みそへのインターフェース</summary> </entry> ... </feed>
レスポンスは Atom で定義されるいわゆる Atom フィードになります。 このフィードには最近登録したブックマークが 20 件入っています。 フィードに含まれるそれぞれのエントリには link 要素によって表現される ハイパーリンクがいくつか含まれます。
もっとも重要なリンクは EditURI へのリンクです。
<link rel="service.edit" type="application/x.atom+xml" href="http://b.hatena.ne.jp/atom/edit/187800" title="何はなくとも XML InfoSet: アウトプットの定義" />
前回、いろいろと操作したブックマークリソースの URI がこの EditURI になります。
その他にもリンクがあります。 一つ目は、このブックマークのターゲット、 ブックマークした Web ページへのリンクです。
<link rel="related" type="text/html" href="http://luckypines.blogspot.com/2005/04/blog-post_28.html" />
もうひとつは、このブックマークリソースの HTML 表現、 つまり人間用の Web ページへのリンクです。
<link rel="alternate" type="text/html" href="http://b.hatena.ne.jp/yohei/20050429#187800" />
それぞれのリンクは、別のリソースの URI を指定していますので、 リソースに HTTP メソッドを適用することができます。 どの HTTP メソッドを適用できるかは仕様しだいです。 たとえば EditURI であれば、GET/PUT/DELETE が適用できることが、 明記されています。
過去のブックマークの取得
前節で解説した FeedURI は現状のはてなブックマーク AtomAPI でサポートされている機能です。 しかし、現在のフィードの実装では最新の20件しか取得することができません。 それ以前のブックマークを取得する方法を検討してみましょう。
この目的を達成するにはいくつかの方法が考えられると思いますが、 今回は検索インターフェースをつける方法をご紹介します。
ここで登場するリソースは、「検索結果」リソースです。 検索結果リソースはこれまでみてきたリソースとは少し趣が異なります。 たとえばキーワード "REST" を検索した検索結果の URI は以下のようになります。
http://b.hatena.ne.jp/atom/search?q=REST
また、キーワード "XML" を検索した検索結果の URI は以下のようになります。
http://b.hatena.ne.jp/atom/search?q=XML
サンプルで明らかだと思いますが、重要なのは URI の "?q=" の部分です。 これは問合せ文字列(query string)と呼びます。 通常 URI はクライアントで構築することはありませんが、 この問合せ文字列だけはクライアント側で操作することができます。 その結果生成された URI はそのキーワードの検索結果の URI を示します。
空文字列のキーワードも許可するように実装すれば、 これまで自分が登録したブックマークをすべて含む検索結果のリソースを特定する URI を作ることもできます。
多くの場合、検索結果は膨大な件数になります。 そのため結果を複数に分けるいわゆるページング処理が必要となります。 分割された結果一つ一つがそれぞれリソースとなります。 21番目の結果から20件を表現するリソースの URI はたとえば以下のようになります。
http://b.hatena.ne.jp/atom/search?q=REST&start=21&num=20
同様の情報を検索結果リソース自身にも入れましょう。 XML 名前空間を使えば、標準以外の情報を Atom フィードに組み込むのは簡単です。 都合のよいことに、OpenSearch RSS という規格で、この用途にぴったりのタグを規定してくれています。
GET /atom/search?q=REST&start=1&num=20 HTTP/1.1 Host: b.hatena.ne.jp X-WSSE: UsernameToken Username="yohei",...
HTTP/1.1 200 OK Content-Type: application/x.atom+xml <feed version="0.3" xmlns="http://purl.org/atom/ns#" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xml:lang="ja"> <title>傭兵ブックマーク</title> <link rel="alternate" type="text/html" href="http://b.hatena.ne.jp/yohei/" /> <link rel="service.post" type="application/x.atom+xml" href="http://b.hatena.ne.jp/atom/post" title="傭兵ブックマーク" /> <modified>2005-04-29T21:43:51+09:00</modified> <author> <name>yohei</name> </author> <id>tag:hatena.ne.jp,2005:bookmark-yohei</id> <generator url="http://b.hatena.ne.jp/" version="0.1">Hatena::Bookmark</generator> <openSearch:totalResults>24</openSearch:totalResults> <openSearch:startIndex>1</openSearch:startIndex> <openSearch:itemsPerPage>20</openSearch:itemsPerPage> ... <:/feed>
あまり詳しく説明しませんが、この検索結果は24件あって、 そのうちここでは1番から20件分を表示している、ということになります。 21番から24番までの検索結果のリソースへのリンクをつけてもよいのですが、 openSearch には適当なタグがなかったのでしていません。 とりあえずクライアント側で問合せ文字列を構築することで対応できます。
なんだか長くなってしまったので、今回はここまで。 次回も続けて、今度はもっと応用的な REST におけるハイパーリンクとハイパーメディアの可能性について解説したいと思います。
大変勉強になります。1点ご質問させていただいてもよろしいでしょうか。
返信削除「通常 URI はクライアントで構築することはありませんが、この問合せ文字列だけはクライアント側で操作することができます。」ということは、逆にいうと「表示件数等のクライアント側で操作する見た目の情報等以外、Atom フィードのリンク URI でパラメータを使用することは望ましくない」ということになりますでしょうか?
具体的には、仮に下の URI で user1 についてのブックマークリストの feed が取得される場合、
http://b.hatena.ne.jp/atom/user1/feed
のかわりに
http://b.hatena.ne.jp/atom/feed?username=user1
などを使うのは望ましくないが、
http://b.hatena.ne.jp/atom/user1/feed?max=20
などは OK という考え方になりますでしょうか?
(このサービスにおいて、検索のキーワードではなく対象リソースの識別であるというほど、ユーザという概念が強く意識されているという場合において)
こまつさんの理解であっていると思います。なぜ URI をクライアント側で組み立てるべきでないかというと、クライアントとサーバの間の結合を疎にするためです。クライアントが知らなければならないパラメータが多ければ多いほど、サービス側のアップデートが大変になります。
返信削除