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*)

参考文献/リソース