- request 20170131.1747(in MELPA)
- Compatible layer for URL request in Emacs
概要
この偉大なライブラリを、なぜみんな知らないのでしょうか。
Emacsにはビルトインで url-retrieve というネットアクセス関数があり、
EmacsでHTTPアクセスをする場合はそれを使うのが普通です。
これは HTTPクライアント としてはお粗末な実装で、
しばしば意味不明な問題に遭遇するし、DNS解決の際には固まります。
それならば curl や wget を外部プロセスとして呼び出した方が、
固まらない上に、より信頼でき、より高機能です。
「餅は餅屋」ということで専用の外部プログラムに頼るのは
本来のEmacsの精神、Unixの精神に沿っています。
しかしWindowsにはそれらがインストールされていないので
それがわかっていてもurl-retrieveに固執してしまいます。
せっかくcurlを持っているUnix系OSのユーザからすれば
有難迷惑なもので、url-retrieveの不自由さにイライラします。
なぜ強力な武器があるのに使わないのですか!
もったいないじゃないですか!
request.el は、url-retrieveとcurlのバックエンドに対応した
ネットワークアクセスを行うライブラリです。
curlがインストールされていればそれを使い、
されていなければurl-retrieveにfallbackします。
しかもparserも定義でき、より宣言的で強力な表現ができます。
elispプログラマはより強力な表現力を亨受でき、
curlを持っているユーザはそれを使って快適なネットアクセスができ、
持っていないならurl-retrieveを使ってくれるようになります。
おまけにEmacs23.1という化石にも対応しているので
古いバージョンのEmacsでも問題なく動きます。
request.elを使えばみんな幸せになれるわけです。
結論は
url-retrieveなんか捨てて、今すぐrequestに書き換えろ!
ということです。
インストール
パッケージシステムを初めて使う人は
以下の設定を ~/.emacs.d/init.el の
先頭に加えてください。
(package-initialize) (setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/") ("melpa" . "http://melpa.org/packages/") ("org" . "http://orgmode.org/elpa/")))
初めてrequestを使う方は
以下のコマンドを実行します。
M-x package-install request
アップグレードする方は、
以下のコマンドでアップグレードしてください。
そのためにはpackage-utilsパッケージが必要です。
M-x package-install package-utils (初めてアップグレードする場合のみ) M-x package-utils-upgrade-by-name request
使用例
代表的な使用例を見れば、使い方が見えてきます。
仕様は<f1> f requestで見られます。
コールバック関数はたくさんのキーワード引数を持つので、
function*、(cl-function)と&allow-other-keysを使います。
サイトの内容をそのままバッファに表示する例
(request "http://rubikitch.com/" :parser 'buffer-string :complete (function* (lambda (&key data &allow-other-keys) (switch-to-buffer "*request-result*") (erase-buffer) (insert data))))
parserにbuffer-stringを指定することで、
HTMLをそのままcomplete(コールバック)のdataに渡せます。
completeはどんな状況でも実行されますが、
successで正常なとき、errorでエラーのときに実行されます。
サイトの内容をw3mで整形してバッファに表示する例
(request "http://rubikitch.com/" :parser (lambda () (shell-command-on-region (point-min) (point-max) "w3m -dump -T text/html" (current-buffer) t) (buffer-string)) :complete (function* (lambda (&key data &allow-other-keys) (switch-to-buffer "*request-result*") (erase-buffer) (insert data))))
parserはネットワークアクセスの結果をカレントバッファにして
実行するので、 shell-command-on-region でw3mを通して、
(buffer-string)でその内容を文字列にします。
GET でデータを送る
http.el(レビュー) で示したようにHTTPクライアント開発支援サイトの
http://httpbin.org/ を使ってみましょう。
ここからはREADMEの例です。
(request "http://httpbin.org/get" :params '(("key" . "value") ("key2" . "value2")) :parser 'json-read :success (function* (lambda (&key data &allow-other-keys) (message "I sent: %S" (assoc-default 'args data)))))
評価すると
I sent: ((key2 . "value2") (key . "value"))
と出ます。
POST
(request "http://httpbin.org/post" :type "POST" :data '(("key" . "value") ("key2" . "value2")) ;; :data "key=value&key2=value2" ; this is equivalent :parser 'json-read :success (function* (lambda (&key data &allow-other-keys) (message "I sent: %S" (assoc-default 'form data)))))
評価すると
I sent: ((key2 . "value2") (key . "value"))
と出ます。
POST file (カレントバッファの全内容が送信されます!!)
(request "http://httpbin.org/post" :type "POST" :files `(("current buffer" . ,(current-buffer)) ("data" . ("data.csv" :data "1,2,3\n4,5,6\n"))) :parser 'json-read :success (function* (lambda (&key data &allow-other-keys) (message "I sent: %S" (assoc-default 'files data)))))
このコードのみを書いたバッファでの実行結果
I sent: ((data . "1,2,3 4,5,6 ") (current\ buffer . "(request \"http://httpbin.org/post\" :type \"POST\" :files `((\"current buffer\" . ,(current-buffer)) (\"data\" . (\"data.csv\" :data \"1,2,3\\n4,5,6\\n\"))) :parser 'json-read :success (function* (lambda (&key data &allow-other-keys) (message \"I sent: %S\" (assoc-default 'files data))))) "))
HTTP 418を返す場合のコールバック
(request "http://httpbin.org/status/418" :parser 'buffer-string :success (function* (lambda (&key data &allow-other-keys) (when data (with-current-buffer (get-buffer-create "*request demo*") (erase-buffer) (insert data) (pop-to-buffer (current-buffer)))))) :error (function* (lambda (&key error-thrown &allow-other-keys&rest _) (message "Got error: %S" error-thrown))) :complete (lambda (&rest _) (message "Finished!")) :status-code '((400 . (lambda (&rest _) (message "Got 400."))) (418 . (lambda (&rest _) (message "Got 418.")))))
結果を*Message*バッファで見てみると
Got 418. Finished!
が表示されたことがわかります。
HTTP 200(正常)を返す場合のコールバック
(request "http://httpbin.org/status/200" :parser 'buffer-string :success (function* (lambda (&key data &allow-other-keys) (when data (with-current-buffer (get-buffer-create "*request demo*") (erase-buffer) (insert data) (pop-to-buffer (current-buffer)))))) :error (function* (lambda (&key error-thrown &allow-other-keys&rest _) (message "Got error: %S" error-thrown))) :complete (lambda (&rest _) (message "Finished!")) :status-code '((400 . (lambda (&rest _) (message "Got 400."))) (418 . (lambda (&rest _) (message "Got 418.")))))
Finished!
とエコーエリアに表示され、 *request demo*バッファが出てきます。
http://httpbin.org/status/200 自体が空の内容なので
バッファの内容も空です。
HTTP 400を返す場合のコールバック
(request "http://httpbin.org/status/400" :parser 'buffer-string :success (function* (lambda (&key data &allow-other-keys) (when data (with-current-buffer (get-buffer-create "*request demo*") (erase-buffer) (insert data) (pop-to-buffer (current-buffer)))))) :error (function* (lambda (&key error-thrown &allow-other-keys&rest _) (message "Got error: %S" error-thrown))) :complete (lambda (&rest _) (message "Finished!")) :status-code '((400 . (lambda (&rest _) (message "Got 400."))) (418 . (lambda (&rest _) (message "Got 418.")))))
結果を*Message*バッファで見てみると
Got 400. Finished!
が表示されたことがわかります。
Atomにxml parserを通し、最初の要素を得る例
(request "https://github.com/tkf/emacs-request/commits/master.atom" ;; Parse XML in response body: :parser (lambda () (libxml-parse-xml-region (point) (point-max))) :success (function* (lambda (&key data &allow-other-keys) ;; Just don't look at this function.... (let ((get (lambda (node &rest names) (if names (apply get (first (xml-get-children node (car names))) (cdr names)) (first (xml-node-children node)))))) (message "Latest commit: %s (by %s)" (funcall get data 'entry 'title) (funcall get data 'entry 'author 'name))))))
結果
Latest commit: Merge pull request #14 from myuhe/request--curl-write-out-template (by tkf)
JSONをPUTする
(request "http://httpbin.org/put" :type "PUT" :data (json-encode '(("key" . "value") ("key2" . "value2"))) :headers '(("Content-Type" . "application/json")) :parser 'json-read :success (function* (lambda (&key data &allow-other-keys) (message "I sent: %S" (assoc-default 'json data)))))
結果
I sent: ((key2 . "value2") (key . "value"))
本サイト内の関連パッケージ
本日もお読みいただき、ありがとうございました。参考になれば嬉しいです。