はてなさんがダイアリーのAtomPubインターフェースをリリースしていました。
私は AtomPub が大好きなので、少しだけ試してみました。簡単にレビューを書こうと思ってエントリを起したのですが、意外と長くなりそうなので3部構成でお送りします。まずは実装編です。
認証
とりあえず普通のGETリクエストをサービス文書に送ってみます。http://d.hatena.ne.jp/{hatena-id}/atom がサービス文書の URI です。
GET http://d.hatena.ne.jp/yohei/atom HTTP/1.1
Accept: */*
Host: d.hatena.ne.jp
HTTP/1.0 401 Unauthorized
Date: Fri, 19 Sep 2008 07:00:10 GMT
Server: Apache/2.2.3 (CentOS)
WWW-Authenticate: WSSE profile="UsernameToken"
Content-Length: 16
Content-Type: text/plain;charset=utf-8
Set-Cookie: b=xxx; path=/; expires=Thu, 14-Sep-28 05:05:06 GMT; domain=.hatena.ne.jp
Vary: Accept-Encoding
401 Unauthorized
401 が返ってきました。WWW-Authenticate ヘッダによれば、WSSE 認証が必要なようです。
レスポンスヘッダで一つ気になるのが、なぜか Set-Cookie ヘッダがあることです。Cookie 認証のなごりでしょうか…。セキュリティの観点からもこのヘッダはなくした方がよさそうです。ちなみに expires の年が2桁で 28 なのもちょっと困りますね。UIの方は普通の Cookie を返すので、何かのバグなんでしょうか。以下の例でも全て Set-Cookie ヘッダが返ってくるのですが、省略しています。
サービス文書
ということで、X-WSSE ヘッダを加えてサービス文書を GET してみます。以下、全てのリクエストで認証が必要ですが、長いので、次以降の例ではこのヘッダは省略します。
GET http://d.hatena.ne.jp/yohei/atom HTTP/1.1
Accept: */*
X-Wsse: UsernameToken Username="yohei", PasswordDigest="foo", Nonce="bar", Created="2008-09-19T22:53:55+09:00"
Host: d.hatena.ne.jp
HTTP/1.0 200 OK
Date: Fri, 19 Sep 2008 05:05:06 GMT
Server: Apache/2.2.3 (CentOS)
Content-Length: 676
Content-Type: application/atomsvc+xml;charset=utf-8
Vary: Accept-Encoding
<?xml version="1.0" encoding="utf-8"?>
<service xmlns="http://www.w3.org/2007/app">
<workspace>
<atom:title xmlns:atom="http://www.w3.org/2005/Atom">Hatena::Diary - yohei</atom:title>
<collection href="http://d.hatena.ne.jp/yohei/atom/draft">
<atom:title xmlns:atom="http://www.w3.org/2005/Atom">yoheiさんの下書き</atom:title>
<accept>application/atom+xml;type=entry</accept>
</collection>
<collection href="http://d.hatena.ne.jp/yohei/atom/blog">
<atom:title xmlns:atom="http://www.w3.org/2005/Atom">yoheiさんの日記</atom:title>
<accept>application/atom+xml;type=entry</accept>
</collection>
</workspace>
</service>
サービス文書が取れました。はてなダイアリーAtomPub では二つのコレクションリソースが用意されているのがわかります。draftコレクション(http://d.hatena.ne.jp/{hatena-id}/atom/draft)とblogコレクション(http://d.hatena.ne.jp/{hatena-id}/atom/blog)です。両方とも、Atom エントリ文書が POST できることがわかります。
ちなみに HEAD してみると…
HEAD http://d.hatena.ne.jp/yohei/atom HTTP/1.1
Accept: */*
Host: d.hatena.ne.jp
HTTP/1.0 200 OK
Date: Fri, 19 Sep 2008 05:07:26 GMT
Server: Apache/2.2.3 (CentOS)
Content-Length: 676
Content-Type: application/atomsvc+xml;charset=utf-8
Vary: Accept-Encoding
nil
HEAD リクエストへのレスポンスなのに、ボディに “nil” とあるのが残念です。
コレクションリソース
次にコレクションリソースです。まずは GET してみました。
GET http://d.hatena.ne.jp/yohei/atom/blog HTTP/1.1
Accept: */*
Host: d.hatena.ne.jp
HTTP/1.0 200 OK
Date: Fri, 19 Sep 2008 05:34:08 GMT
Server: Apache/2.2.3 (CentOS)
Content-Length: 20722
Content-Type: application/atom+xml;charset=type=feed
Vary: Accept-Encoding
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<updated>2006-11-24T00:00:00+09:00</updated>
<id>tag:d.hatena.ne.jp,2006:diary-yohei</id>
<title>傭兵日記</title>
<author>
<name>yohei</name>
</author>
<link rel="self" href="http://d.hatena.ne.jp/yohei/atom/blog"/>
<link rel="next" href="http://d.hatena.ne.jp/yohei/atom/blog?page=2"/>
<entry>
...
プレーンな Atom フィードが返ってきました。rel=”next” なリンクもありますね。これは便利です。
ということで、2ページ目を GET してみました。
GET http://d.hatena.ne.jp/yohei/atom/blog?page=2 HTTP/1.1
Accept: */*
Host: d.hatena.ne.jp
HTTP/1.0 200 OK
Date: Fri, 19 Sep 2008 07:06:51 GMT
Server: Apache/2.2.3 (CentOS)
Content-Length: 24964
Content-Type: application/atom+xml;charset=type=feed
Vary: Accept-Encoding
X-Cache: MISS from unknown
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<updated>2006-11-24T00:00:00+09:00</updated>
<id>tag:d.hatena.ne.jp,2006:diary-yohei</id>
<title>傭兵日記</title>
<author>
<name>yohei</name>
</author>
<link rel="self" href="http://d.hatena.ne.jp/yohei/atom/blog?page=2"/>
<link rel="next" href="http://d.hatena.ne.jp/yohei/atom/blog?page=3"/>
<entry>
...
2ページ目のリソースからは3ページ目にリンクしてますね。ここで惜しいのは、rel=”prev” なリンクがないことです。リンクが片道通行になっちゃってます。
もうひとつ気になったのはフィードの id です。tag スキームを使っているのですが、これに含まれる年がフィードに含まれる最新エントリの年になるようなのです。MT 3.x で同様の問題がありましたが、idは一定の値でないとまずいので、たとえば2008をハードコーディングした方がよいのではないかなと思います。
それから、Content-Type が
application/atom+xml;charset=type=feed
なんですが、これは正しくは
application/atom+xml;charset=utf-8;type=feed
ですね。
ちなみに、コレクションでも HEAD リクエストを送ってみました。
HEAD http://d.hatena.ne.jp/yohei/atom/draft HTTP/1.1
Accept: */*
Host: d.hatena.ne.jp
HTTP/1.0 405 Method Not Allowed
Date: Fri, 19 Sep 2008 05:16:12 GMT
Server: Apache/2.2.3 (CentOS)
Allow: GET, POST
Content-Length: 22
Content-Type: application/atom+xml;charset=type=entry
Vary: Accept-Encoding
X-Cache: MISS from unknown
nil
おおー、405 が返ってきました。しかもちゃんと Allow ヘッダもあります。ただ、Content-Type のパラメータが charset=type=entry なのはおかしいですね。entry ではないですし、charset=utf-8;type=feed であるべきです。
エントリのPOST
ではエントリを POST してみましょう。まずは draft コレクションに POST してみました。
POST http://d.hatena.ne.jp/yohei/atom/draft HTTP/1.1
Accept: */*
Content-Type: application/atom+xml;type=entry;charset=utf-8
Content-Length: 131
Host: d.hatena.ne.jp
<entry xmlns="http://www.w3.org/2005/Atom">
<title>test</title>
<content type="text">- test
-てすと</content>
</entry>
HTTP/1.0 201 Created
Date: Fri, 19 Sep 2008 07:45:24 GMT
Server: Apache/2.2.3 (CentOS)
Location: http://d.hatena.ne.jp/yohei/atom/draft/1221810325
Content-Length: 549
Content-Type: application/atom+xml;charset=type=entry
Vary: Accept-Encoding
<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom">
<id>tag:d.hatena.ne.jp,2008:diary-draft-yohei-1221810325</id>
<link rel="edit" href="http://d.hatena.ne.jp/yohei/atom/draft/1221810325"/>
<author>
<name>yohei</name>
</author>
<title>test</title>
<updated>2008-09-19T16:45:25+09:00</updated>
<published>2008-09-19T16:45:25+09:00</published>
<app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-19T16:45:25+09:00</app:edited>
<content type="text/plain">- test
-てすと</content>
</entry>
無事エントリが作成されます。ちなみに、content 要素の内容は draft コレクションでははてな記法の文字列が text/plain 形式で入り、blog コレクションではhtmlに変換済みの文字列が text/html で入ります。そして、blog コレクションでのはてな記法の文字列は hatena:syntax 要素に入ってくるそうです。これは最初わからず、ちょっとはまりました…
ただ、ちょっと気になるのは content 要素の type 属性の値です。RFC 4287 では text/html/xhtml or MIME タイプを取ることになっており、ここでは text/plain ではなく text が、そして text/html ではなく html が望ましいですね。
その他の操作
とりあえず仕様書どおりに実装すれば PUT/DELETE はできました。blog コレクションでも PUT するときは content 要素にはてな記法を入れるというのにははまりましたが…。これについては設計編で詳しく触れたいと思います。
それから、ちょっとした不正 URI のテスト(全てGET)をしてみましたが、以下のような結果でした。
- http://d.hatena.ne.jp/{hatena-id}/ato -> 302
- http://d.hatena.ne.jp/{hatena-id}/atom/blo -> 404
- http://d.hatena.ne.jp/{hatena-id}/atom/blog/20 -> 404
- http://d.hatena.ne.jp/{hatena-id}/atom/blog/{yyymmdd}/foo -> 403
これらは全て 404 が望ましいと思います。
また、全体的に Content-Type が正しく設定されていない場面がみうけられます。
たとえば DELETE のレスポンスが空なのに text/html;charset=utf-8 だったり、400/403 が返るときにプレーンテキストなのにapplication/atom+xml;type=entry が返ってきたりなどです。
この辺はテストケースの不足だと思いますが、AtomPub には Tim Bray が作った Ape や Joe Gregorio の Atom Publishing Protocol Test Suite など、テストケースが用意されているので、これらを使ってテストするといいと思います。