はじめてのClack - Common LispでWeb開発
本記事は、原著者の許諾のもと、翻訳・掲載しています。 Getting started with clack / Jason Miller
チュートリアルを始める前に、roswellをインストールしてください。
rowellをインストール後、TerminalでREPLを起動すると、準備完了です。
$ ros run *
では、チュートリアルに進みましょう。
はじめてのClack
Clackは、様々なLisp Webサーバを統一して利用するためのシンプルなフレームワークです。Clackに関する文献が少ないので、このページでは、Clackの使い方について書きます。
依存環境を読み込む
* (ql:quickload '(clack alexandria optima)) * (use-package :optima)
サーバを起動する
clackup
に必要な引数はapplication
だけです。application
は、(http-response-code
http-headers-alist
&optional
`body)をリストで返す必要があります。
body
には、(unsigned-byte 8)
のベクタ、パス名、文字列のリストを書くことができます。
(defparameter *clack-server* (clack:clackup (lambda (env) '(200 nil ("Hello, World!")))))
Hunchentoot server is started. Listening on localhost:5000.
curlでから始めましょう:
curl -s http://localhost:5000
Hello, World!
サーバを停止する
(clack:stop *clack-server*)
ハンドラを再定義する
サーバを毎回再起動するのは苦痛なので、再定義ができるハンドラを定義しましょう:
(defun handler (env) '(200 nil ("Hello World, redefinable!")))
そしてサーバを起動します。再定義が可能になるように、関数を名前で呼び出します:
(defparameter *clack-server* (clack:clackup (lambda (env) (funcall 'handler env))))
Hunchentoot server is started. Listening on localhost:5000.
正常に動作するか確認しましょう:
curl -s http://localhost:5000
Hello World, redefinable!
では、再定義して、環境内でどうになっているかを見てみましょう:
(defun handler (env) `(200 nil (,(prin1-to-string env))))
結果を確認しましょう...
curl -s http://localhost:5000
(:REQUEST-METHOD :GET :SCRIPT-NAME "" :PATH-INFO "/" :SERVER-NAME "localhost" :SERVER-PORT 5000 :SERVER-PROTOCOL :HTTP/1.1 :REQUEST-URI "/" :URL-SCHEME "http" :REMOTE-ADDR "127.0.0.1" :REMOTE-PORT 53824 :QUERY-STRING NIL :RAW-BODY #<FLEXI-STREAMS:FLEXI-IO-STREAM {1021B536E3}> :CONTENT-LENGTH NIL :CONTENT-TYPE NIL :CLACK.STREAMING T :CLACK.IO #<CLACK.HANDLER.HUNCHENTOOT::CLIENT {1021B537F3}> :HEADERS #<HASH-TABLE :TEST EQUAL :COUNT 3 {1021B53C13}>)
これがclackの中心部であり、環境を表すplistです。
環境についてのドキュメントは、lack documentにあります。
plistであることにより、捕捉される値は、destructuring-bind
で処理できます。
(defun handler (env) (destructuring-bind (&key request-method path-info request-uri query-string headers &allow-other-keys) env `(200 nil (,(format nil "Method: ~S Path: ~S URI: ~A Query: ~S~%Headers: ~S" request-method path-info request-uri query-string (alexandria:hash-table-alist headers))))))
curl -s http://localhost:5000
Method: :GET Path: "/" URI: / Query: NIL Headers: (("accept" . "*/*") ("user-agent" . "curl/7.53.0") ("host" . "localhost:5000"))
Optimaを使うと便利です:
(defun handler (env) (optima:match env ((guard (property :path-info path) (alexandria:starts-with-subseq "/foo/" path)) `(200 nil (,(format nil "The path '~A' is in /foo/~%" path)))) ((guard (property :path-info path) (alexandria:starts-with-subseq "/bar/" path)) `(200 nil (,(format nil "The path '~A' is in /bar/~%" path)))) ((property :path-info path) `(404 nil (,(format nil "Path ~A not found~%" path))))))
curl -s http://localhost:5000/foo/quux curl -s http://localhost:5000/bar/quux curl -s http://localhost:5000/baz/quux
The path '/foo/quux' is in /foo/ The path '/bar/quux' is in /bar/ Path /baz/quux not found
Public API
CLACK:CLACKUP
シンタックス:
*clackup* app &key server port debug silent use-thread use-default-middlewares &allow-other-keys => ハンドラ
引数と値:
app - 1つの引数の関数を示します。
lack.component:lack-component
のサブクラスです。パス名か文字列です。server - シンボル。初期値は、
:hunchentoot
port - 整数。初期値は、
5000
。debug - 論理値。初期値は、
t
。silent - 論理値。初期値は、
nil
。use-thread - 論理値。初期値は、スレッドサポートのシステムでは
t
、そうでない場合はnil
。use-default-middlewares - 論理値。初期値は、
t
。handler - clack.handler::handler
説明:
clackup
は、指定されたサーバとport
をバックエンドとして使いながら、サーバを起動します。
app
は、次のように、サーバがハンドラの連鎖を構成するために使われます:
もし
app
が関数の場合は、app
は直接つかわれます。そして、リクエスト環境を唯一の引数として、それぞれのリクエストに対して、呼び出されます。app
がlack.component:lack-component
のサブクラスの場合、(lack.component:callapp
environment
)がリクエスト毎に呼び出されます。app
がパス名の場合、lispファイルとして扱われて実行されます。ファイルにある最後の式の結果が、上のように使われます。app
が文字列の場合、パス名に変換されて、上のように使われます。use-default-middlewares
がtrue
の場合、app
はデフォルトのミドルウェアに内包(wrap)されます。
server
は、利用するバックエンドを指定します。バックエンドが見つからない場合、clackup
は、quicklisp
かasdf
を用いて、バックエンドを読み込もうとします。
port
は、サービスを受けつけるポートを特定します。
debug
は、デバッグモードを指定します。ここでの結果は、バックエンドによって異なりますが、appの本体で生じる全てのエラーは、false
の場合、500のシスポンスを返すことで処理します。
silent
は、ステータスのメッセージが出ないように抑えます。
use-thread
がtrue
の場合, 別のスレッドでバックエンドが起動します。
(記事 終)
(参考) lack document より
環境 (Environment)
application
は、環境(Environment)を受け取ります。中身はplistであり、以下のキーを含みます:
:request-method (必須, キーワード) HTTP リクエストメソッドとして、:GET :HEAD :OPTIONS :PUT :POST :DELETEを指定します。 :script-name (必須, 文字列) リクエストURIパスの最初の部分であり、Clack applicationに対応します。 このキーの値は、クライアントがサーバーのルートでアクセスする場合、空の文字列になります。 そうでない場合は、スラッシュ/から始めて指定します。 :path-info (必須, 文字列) リクエストURIパスの残りです。トレイリングスラッシュがない場合、空の文字列になります。 :query-string (オプション, 文字列) もし URIに?クエリがある場合、対応します。 :url-scheme (必須, 文字列) リクエストされるURIにより、"http"か"https"になります。 :server-name (必須, 文字列) 名前解決されるサーバ名か、サーバのIPアドレスです。 :server-port (必須, 整数) リクエストが処理されているポート番号です。 :server-protocol (必須, キーワード) クライアントがリクエストを送るプロトコルのバージョンです。 :HTTP/1.0か :HTTP/1.1 のことが多いです。 :request-uri (必須, 文字列) リクエストのURIです. 常に"/"で始まります。 :raw-body (オプション, ストリーム) リクエストの新しいbody部です。 :remote-addr (必須, 文字列) リモート用のアドレスです。 :remote-port (必須, 整数) リモート用のポートです。 :content-type (必須, 文字列) Content-Typeのヘッダー情報です。 :content-length (オプション, 整数) Content-Lengthのヘッダーの値です。 :headers (必須, ハッシュテーブル) ヘッダーのハッシュテーブルです。