前回の(1)はこちらから。
さまざまなPlack::Middleware
Plack::Middlewareの3パターンについて具体的なモジュールを見ながら理解を深めたところで、CPANにアップロードされているPlack::Middlewareの中から、筆者が注目しているものをピックアップして紹介したいと思います。
なお、本節で登場するenable関数は、Plack::Middlewareをロードして有効にするもので、第1引数にミドルウェアの名前、第2引数以降にオプションを取ります。
開発を捗らせるPlack::Middleware
プロダクション環境では必要ありませんが、開発環境で使うと捗るPlack::Middlewareがあります。
DebugLogging──コンソールにデバッグ情報を出力する
Plack::Middleware::DebugLoggingは、リクエストパスやHTTPヘッダといったリクエストとレスポンスに関連するパラメータをコンソールに表示してくれます。
たとえば、Content-type: application/json
でJSONをリクエストすると、デコードしたものがダンプされるので開発が捗ります。
上記のようなリクエストを投げると、次のようなデバッグ出力を得ることができます。
StackTrace::LinkedSource──スタックトレースからソースをブラウザで表示する
Plackの開発環境では、エラーが発生した場合、HTMLで見やすく整形されたスタックトレースが表示されます。Plack::Middleware::StackTrace::LinkedSourceは、そのスタックトレースに含まれるモジュールのパスからソースコードをブラウザで直接参照できるようにしてくれます(図2)。
ローカルの実行環境ならソースコードを参照するのも難しくないと思いますが、デザイナーの作業環境やQA(Quality Assuarance、品質保証)環境といった別環境でエラーが起きた場合に、ブラウザのスタックトレースからそのままコードが追えるので便利です。
HTTPヘッダを扱うPlack::Middleware
WebアプリケーションでHTTPヘッダを扱う場面はたくさんあります。そうしたものの中には、アプリケーションでその都度扱うよりも、Plack::Middlewareで一括して面倒を見てもらったほうが楽なものがあります。
ETag──ETagヘッダを自動で付与する
Plack::Middleware::ETagは、コンテンツの変更を検知するための一意の識別子であるETagヘッダをレスポンスに付与します。
アプリケーションで静的ファイルを配信している場合は、デフォルトでinode(ファイルの管理番号)、mtime(最終更新日時)、size(ファイルサイズ)の情報から値を生成します。動的コンテンツの場合は、コンテンツのダイジェストハッシュを値とします。モジュールの利用者は通常それらを意識することなく、上記の1行を書くだけで対応できます。
ConditionalGET──ステータスコード304に対応する
Plack::Middleware::ConditionalGETは、リクエストにIf-None-MatchヘッダやIf-Modified-Sinceヘッダがある場合に、レスポンスのETagヘッダやLast-Modifiedヘッダと比較して同じ値の場合は、更新がない旨を表すステータスコード304(Not Modified)を自動的に返却します。ステータスコード304はコンテンツ自体の送信が不要なので、転送量を削減できます。先に紹介したPlack::Middleware::ETagとセットで利用すると便利です。
CrossOrigin──CORSに対応する
Plack::Middleware::CrossOriginは、クロスドメインでXMLHttpRequestを行いたい場合などに必要なCORS(Cross-Origin Resource Sharing)に対応できます。
CORSに対応するヘッダは、一般的にApacheやnginxなどで設定することが多いかもしれませんが、たとえばリクエストパスに応じて処理をするというような込み入った条件のある場面では、Plack::Middlewareで行ったほうが簡単かもしれません。
プロダクションに常時投入したいPlack::Middleware
ここでは、プロダクションに常時投入したいPlack::Middlewareを紹介します。
次に紹介する2つのPlack::Middlewareは、プロダクション環境に素直に投入すると処理性能がいくらか劣化します。とはいえ、取得できる情報はいざというときに重要なものですので、できれば常時運用で投入しておきたいところです。常時運用で投入するための工夫としては、たとえば、特定サーバのみで有効になるようにしたり、特定プロセスでのみ有効にしたり、特定のエンドポイントでのみ有効にするといったアプローチが考えられます。サーバ構成の状況を見ながら対応するとよいでしょう。
MemoryUsage──メモリの使用量を見る
Plack::Middleware::MemoryUsageは、ロードされたモジュールごとのメモリ使用量を計測できます。
出力内容はcallbackオプションにコードリファレンスを自分で書く必要がありますが、上記のようにSYNOPSISをそのままコピーして動かせます。出力例は次のようになります。
Profiler::NYTProf──プロファイリングを取得する
Plack::Middleware::Profiler::NYTProfは、PerlのデファクトスタンダードプロファイラであるところのDevel::NYTProfをPlackアプリケーションで利用するためのミドルウェアです。
prefork型のアプリケーションにおいて、親プロセスのプロファイリングを取得しないで、子プロセスだけのプロファイリングを取得できます。また、プロファイルの有効無効や結果ファイル名を動的にカスタマイズすることもできます。
Plack::Middlewareの利用時のTips
さて、ここからはPlack::Middlewareを利用するときのTipsを紹介します。
Plack::Middlewareのロード
Plack::Middlewareのロード方法は2つあります。
1つ目は、Plack::Middlewareを普通のモジュールと同じようにuseしたうえで、wrapメソッドによって追加していく方法です。
2つ目は、次のようにPlack::BuilderのDSL(Domain Specific Language、ドメイン特化言語)を利用したロード方法です。
DSLを使ったロード方法はuseを書く必要がなく、"Plack::Middleware::"
の部分を省略できるなど、短く簡潔に記述できます。
また、Plack::BuilderのDSLはplackup
コマンドでも直接使えるので、Plackアプリケーションをワンライナーで起動するときに便利です。
新しいミドルウェアを試してみる場合などに、いきなりアプリケーションに投入するのではなく、plackup
コマンドでさくっと試すというのが簡単でお勧めです。
開発環境とプロダクション環境での切り替え
開発環境とプロダクション環境で利用するPlack::Middlewareを切り替えたい場合は、次のようにPlack::Builderが提供するenable_ifを使います。
ただし、このコードには小さな落とし穴があります。enable_ifの条件の判定はランタイムであるという点です。つまり、ミドルウェアのモジュール自体はロードされていて、条件判定はリクエストごとに実行されるのです。
気になる人は、次のようにenable自体を条件判定で囲んでしまうようにすれば、ロード自体をやめることができます。
実行条件の複雑化への対処
enable_ifの引数に渡すブロックの条件判定は、ともすると複雑になりがちです。そうした場合には、Plack::Builder::Conditionalsの利用を検討してみましょう。リモートアドレスやリクエストパス、HTTPヘッダ、ユーザーエージェントなどの条件を見て、ミドルウェアを実行するためのDSLを提供してくれます。
Plack::Middlewareのロード順と実行順序
先述したとおりPlack::Middlewareには、アプリケーションの処理の前に仕事をするもの、あとに仕事をするもの、前後両方で仕事をするものという3パターンがあります。これは、複数のPlack::Middlewareを利用する際に少し注意が必要です。
次の2つのPlack::Middlewareがあるとします。1つ目はPlack::Middleware::Fooで、2つ目はPlack::Middleware::Barです。いずれも、$self->app
の前後で標準出力にミドルウェア名を出力するだけです。
この2つのミドルウェアを次のように利用します。Foo➡Barの順にenableしています。
これを実行すると、標準出力には次のように出力されます。
enableしたのはFoo➡Barの順でしたが、出力の中でAfterのほうはBar➡Fooの順になっています。これは、もしかしたら直感と違うという人がいるかもしれません。しかし、Plack::Middlewareはアプリケーションをラップする=包むという言葉どおり、先にロードされたPlack::Middlewareが後続のアプリケーション(ミドルウェア)を内包していくしくみなのです。したがって、$self->app
のあとに書かれたPlack::Middlewareの処理は、ロード順と逆に行われます。複数のPlack::Middlewareを利用する場合は、この処理順に気を付けてロードする必要があります。
<続きの(3)はこちら。>
- 特集1
イミュータブルデータモデルで始める
実践データモデリング
業務の複雑さをシンプルに表現!
- 特集2
いまはじめるFlutter
iOS/Android両対応アプリを開発してみよう
- 特集3
作って学ぶWeb3
ブロックチェーン、スマートコントラクト、NFT