Drakma メモ
DRAKMA で RESTFul な API を叩いてみる。例として「Web を支える技術」の郵便番号検索サービスを使う。
REST-TEST> (let ((uri "http://zip.ricollab.jp/1620846")) ;; 技術評論社 (http-request uri :method :get)) "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"ja\"> <head> <title>〒162-0846</title> </head> <body> <h1>〒162-0846</h1> <dl> <dt>番号</dt> <dd class=\"zipcode\">1620846</dd> <dt>住所</dt> <dd class=\"address\"> <a href=\"/東京都\" class=\"prefecture\">東京都</a> <a href=\"/東京都/新宿区\" class=\"city\">新宿区</a> <a href=\"/東京都/新宿区/市谷左内町\" class=\"town\">市谷左内町</a></dd> <dt>フリガナ</dt> <dd class=\"yomi\"> <a class=\"prefecture\">トウキョウト</a> <a class=\"city\">シンジュクク</a> <a class=\"town\">イチガヤサナイチョウ</a> </dd> </dl> </body> </html> " 200 ((:DATE . "Sun, 28 Aug 2011 00:05:45 GMT") (:SERVER . "Mongrel 1.1.3") (:STATUS . "200 OK") (:X-RUNTIME . "0.00229") (:ETAG . "\"80f47432e3f3eb3d4c0e4ed175789044\"") (:CACHE-CONTROL . "private, max-age=0, must-revalidate") (:CONTENT-TYPE . "text/html; charset=utf-8") (:CONTENT-LENGTH . "674") (:CONNECTION . "close")) #<PURI:URI http://zip.ricollab.jp/1620846> #<FLEXI-STREAMS:FLEXI-IO-STREAM {100452E171}> T "OK" REST-TEST>
JSON フォーマットで取得し、さらに Common Lisp の連想配列にすることもできる。ついでに条件付き GET (更新されている場合のみ取得)を試す例。
(pushnew '("application" . "json") drakma:*text-content-types* :test #'equal) (defun rest-get-test-json (&optional use-etag) (let ((uri "http://zip.ricollab.jp/1620846.json")) (multiple-value-bind (body status-code headers uri2 stream must-close reason-phrase) (http-request uri :method :get :want-stream t :external-format-out :utf-8 :additional-headers (if use-etag `((If-None-Match . ,use-etag)) nil)) (declare (ignore uri2 stream must-close reason-phrase)) (case status-code ((200) (format t "OK status-code: ~a~%headers: ~a~%" status-code headers) (json:decode-json body) ) ((304) (format t "Not Modified status-code: ~a~%headers: ~a~%" status-code headers) ) (t (format t "error: ~a ~%" status-code))) )))
条件付き GET は以下のようになる。一度データを取得し、その際得られるメタデータ(ETag) を保持しておく。次にデータを取得する際に ETag 値を送信すると、サーバ側でデータが更新されていなければ HTTP コード 304 を返してくれる。
REST-TEST> (rest-get-test-json nil) OK status-code: 200 headers: ((DATE . Sun, 28 Aug 2011 00:53:05 GMT) (SERVER . Mongrel 1.1.3) (STATUS . 200 OK) (X-RUNTIME . 0.00216) (ETAG . "5ee26c16987589f683bbda80405cacaf") (CACHE-CONTROL . private, max-age=0, must-revalidate) (CONTENT-TYPE . application/json; charset=utf-8) (CONTENT-LENGTH . 269) (CONNECTION . close)) ((:ZIPCODE . "1620846") (:ADDRESS (:PREFECTURE . "東京都") (:CITY . "新宿区") (:TOWN . "市谷左内町")) (:YOMI (:PREFECTURE . "トウキョウト") (:CITY . "シンジュクク") (:TOWN . "イチガヤサナイチョウ"))) REST-TEST> (rest-get-test-json "\"5ee26c16987589f683bbda80405cacaf\"") Not Modified status-code: 304 headers: ((DATE . Sun, 28 Aug 2011 00:53:16 GMT) (SERVER . Mongrel 1.1.3) (CONNECTION . close) (ETAG . "5ee26c16987589f683bbda80405cacaf") (CACHE-CONTROL . private, max-age=0, must-revalidate)) NIL REST-TEST>
Etag の値はクォート付きであることに注意。
追記:以下を SLIME REPL で実行しておくと、HTTP ヘッダが REPL に表示されるのでよい。DRAKMA のサイトにもデバッグ用に有用とちゃんと書いてあった。
(setq drakma:*header-stream* *standard-output*)
参考文献/リソース
- Drakma (http://weitz.de/drakma/) Common Lisp で書かれた Web Client 。任意のHTTPヘッダも送れるし、日本語も問題ない。quicklisp 対応。
- JSON http://www.json.org/json-ja.html
- cl-json (http://common-lisp.net/project/cl-json/) Common Lisp 用の JSON パーザ(かつジェネレータ)。quicklisp 対応。
- 郵便番号検索サービス http://zip.ricollab.jp/ 「Webを支える技術」で例として提供されている RESTFul なWebサービス。 ricollab サービス利用規約(http://zip.ricollab.jp/terms_of_use.html) と API 仕様(http://zip.ricollab.jp/api.html)を参照すること。
- http://read-eval-print.blogspot.com/2007/12/common-lisp-twitter-api.html Drakma で JSON フォーマットを扱うのに参考になる。drakma:*text-content-types* の設定。
- Apache の Etag http://httpd.apache.org/docs/2.2/ja/mod/core.html#fileetag