はじめに
前回は、メッセージボードアプリケーションを作成し、実際のWSGIアプリケーションについて見てきました。 WSGI を使用した Web アプリケーションの作成がどのようなものかわかって頂けたかと思います。
今回は、 WSGIの仕組みの中でも重要な、「ミドルウェア」という概念を取り上げます。「ミドルウェア」はアプリケーションの機能をコンポーネント化し、コードの再利用性を高めるための概念です。このように書くと、少し難しいもののように思えるかもしれませんが、その仕組みは非常に簡単なものです。
まずは、「ミドルウェア」の概念について説明します。
ミドルウェアとは
ミドルウェアとは、サーバとWSGIアプリケーションの両方のインターフェースを持つオブジェクトです。そのため、Webサーバ側からはWSGIアプリケーションのように見え、WSGIアプリケーション側からはWebサーバのように見えます。ミドルウェアは図1のようにサーバとアプリケーションの中間に位置し、Webサーバからのリクエストに対する処理と、アプリケーションからのレスポンスに対する処理を行います。
たとえば
- リクエストURLに応じたアプリケーションのマッピング
- ユーザ認証
- レスポンスヘッダへの情報追加
- アプリケーションで生成された動的コンテンツのキャッシュ
といったような処理です。
図1では、WebサーバとWSGIアプリケーションの間にミドルウェアの層が幾つも重なっています。ミドルウェアは、それ自身がWSGIアプリケーションのように振る舞うため、「ミドルウェアが呼び出したアプリケーションがミドルウェアで、そのミドルウェアが呼び出したアプリケーションがまたミドルウェアで…」と最終的にWSGIアプリケーションが呼び出されるまで続きます。このミドルウェアの連鎖をミドルウェアスタックといいます。
実際のミドルウェア
ここで、簡単なミドルウェアを例に、処理の流れを追ってみます。
今回は、
- 何もしないミドルウェア
- URLマッピングを行うミドルウェア
の2種類です。
今回使用するサンプルのソースはmiddleware.zip(ZIP圧縮)にあります。適宜ダウンロードしてください。
今回は、第1回、第2回で使用したsample1.pyとmessage.pyを使用しています。middleware.zipにはそれらも含まれているので、展開して同じディレクトリに置いてください。
実行は前回と同様に
とします。
実行した後に、http://localhost:8080/の/helloと/messageにアクセスすると、それぞれ第1回、第2回で作成したアプリケーションが実行されます。それ以外のURLにアクセスした場合は、404エラーが返ってきます。
何もしないミドルウェアも使用していますが、文字通り何もしないので変化がなく、ブラウザからアクセスしても変化はわかりません。確認はソース上のみで可能です。
それでは、それぞれのミドルウェアのソースを見ていきます。
何もしないミドルウェア
最初は、何もしないミドルウェアです。
ミドルウェアの基本的な処理は、コンストラクタで呼び出すアプリケーションを受け取り、自身がアプリケーションとしてサーバ側から呼び出された時に、コンストラクタで受け取ったアプリケーションを呼び出す、というものです。
何もしないミドルウェアは、以下のように使用します。
このようにしておくと、 WebサーバからNopの__call__メソッドが呼び出され、その中でコンストラクタで渡したアプリケーションが呼び出されます。
URLマッピングを行うミドルウェア
次は、実際に機能するミドルウェアの例として、URLマッピングを行うミドルウェアを扱います。具体的には、アクセスされたURLごとにアプリケーションを割り当てるような処理を行います。
たとえば、WSGIアプリケーションにアクセスするための URLがhttp://somedomain.jp/
であったとき、http://somedomain.jp/path1
とhttp://somedomain.jp/path2
で別のアプリケーションに処理を割り振る、といったことができます。
このミドルウェアは、以下のように使用します。
こうしておくと、/path1へのアクセスはapp1に、/path2へのアクセスはapp2に振り分けられるようになります。
それでは実装の解説に入ります。
SelectAppのコンストラクタでは、2つの引数を取っています。tableは、リスト4でコンストラクタに渡しているように、割り当てるURLをキーに、呼び出すアプリケーションを値に持つ辞書オブジェクトです。notfoundは、割り当てるURLが存在しなかったときに呼び出すアプリケーションです。デフォルトでは404を返すアプリケーションが使われます。割り当てテーブルは、キーであるURLの長い順にマッチさせるため、キーと値のタプルをキーが長い順に並べ替えたリストを生成して保持しておきます。
__call__ メソッドでは、リクエストされたURLごとにアプリケーションに処理を割り振ります。environ辞書の中で使用するのは、SCRIPT_NAMEとPATH_INFOです。
SCRIPT_NAMEはURL上でアプリケーションのルートがどの位置にあるかを表します。たとえば、http://localhost/app/path
にあるならば、 SCRIPT_NAME は "/app/path" が入っています。
PATH_INFO は、アプリケーション上の仮想的な URL を表しています。http://localhost/app/path
がアプリケーションのある位置で、http://localhost/app/path/extra
とアクセスした場合、PATH_INFOには"/extra"が入っています。
振り分けは、PATH_INFOとコンストラクタで受け取ったテーブルのパスとを1つずつマッチさせて調べます。その際に、 Pythonの文字列型のメソッドstartswithを使用しています。これでpathinfoの文字列の先頭がpであるかどうかを調べています。
ただし、startswithだけでは'/path'
の振り分けに'/path/path2'
だけでなく'/pathother'
なども含まれてしまいます。なので、 PATH_INFO の文字列の長さがテーブルのパスよりも長い場合は、 pathinfo[len(p)] が / になっているかどうかを調べています。
判定で一致した場合は、 PATH_INFOの先頭からlen(p)分だけ文字を取り除き、SCRIPT_NAMEの末尾にpをそのまま加えて、environ辞書を更新したうえでアプリケーションを呼び出します。こうしておくことで、SelectAppを再帰的に適用して振り分けを行うことができます。
すべてのパスに一致しない場合は、notfoundアプリケーションを呼び出して、そのままreturnします。
まとめ
以上が WSGI アプリケーションにおけるミドルウェアの仕組みです。
このように、機能ごとに完全に独立したミドルウェアを作成し、それらを組み合わせて1つのアプリケーションとすることができるのがミドルウェアの利点です。
このようなミドルウェアは、すでにライブラリとして提供されているものもあります。代表的なミドルウェアライブラリにはpasteがあります。pasteには、今回作成したURLマッパと同機能のミドルウェアや、 gzip圧縮をサポートするミドルウェアなどが存在します。これからWSGIアプリケーションを作成する方は利用してみてください。
今回までで、WSGIの基本的な仕組みの説明は終わりです。
次回は、実際にWSGIアプリケーションを動かすサーバとしてGoogle App Engine(以下GAE)を取り上げます。GAEはGoogleが提供するWebアプリケーションサーバです、GAEを利用すると、自分が作ったWebアプリケーションをGoogleのサーバ上で動かすことができます。作成したアプリケーションを非常に簡単にインターネット上で公開できるので、ぜひ試していただきたいサービスです。