汎用的なPlack::Middlewareを書く
さて、
Plack::Util::Accessor──アクセサの生成
Plack::Util::Accessorを利用することで、
Plack::Middlewareは、file_
がオプションです。
enable "ETag",
file_etag => [qw/size/];
ミドルウェア側からは、
use Plack::Util::Accessor qw/
file_etag
/;
sub call {
my ($self, $env) = @_;
my $file_attr = $self->file_etag;
}
Plack::Util──Plackに関するユーティリティ
Plack::Utilは、
response_cb──遅延レスポンスやストリーミングに対応する
Plackのレスポンスと言えば、response_
です。Plack::Middlewareの中では$self->response_
として利用できます。
sub call {
my ($self, $env) = @_;
my $res = $app->($env);
return Plack::Util::response_cb($res, sub {
my $res = shift;
# do something with $res;
});
}
遅延レスポンスやストリーミングを扱わないアプリケーションの中だけであればレスポンスを直接操作しても問題ないかもしれませんが、response_
を利用することで汎用性を持った実装ができます。
header_*関数群──HTTPヘッダを操作する
Plackレスポンスのヘッダ部分はkey-value形式で並んだARRAYリファレンスです。シンプルな構造なので直接操作してしまいそうになりますが、
これらの中で注意が必要なのはheader_
また、
Plack::Util::header_iter(
['Content-Type' => 'text/plain'],
sub {
my ($http_header_key, $http_header_value) = @_;
# do something
},
);
また、
my $headers = ['Content-Type' => 'text/plain'];
my $h = Plack::Util::headers($headers);
$h->get($key);
if ( $h->exists($key) ) {(省略)}
$h->set($key => $val);
$h->push($key => $val);
$h->remove($key);
$h->iter($code);
is_real_fh──ファイルが実体を持っているかを判定する
Plackレスポンスのボディには、
set_io_path──ファイルハンドルに実体のパスを設定する
ファイルの配信をアプリケーションの前段にいるlighttpdやApacheやnginxに任せるX-Sendfileというしくみがあります。アプリケーション自体による転送がなくなるので配信効率の向上が望めます。Plack::MiddlewareでX-Sendfileに対応するにはPlack::Middleware::XSendfileを利用すると簡単なのですが、
prepare_app──ロード後に一度だけ呼ばれるメソッド
Plack::Middlewareにはprepare_
次のコードではprepare_
package Plack::Middleware::Foo;
use parent qw( Plack::Middleware );
use Plack::Util::Accessor qw( uuid callback );
use Data::UUID;
sub prepare_app {
my $self = shift;
$self->uuid(Data::UUID->new);
unless ($self->callback) {
$self->callback(sub {
my $res = shift;
warn "status:$res->[0]\n";
});
}
}
sub call {
my ($self, $env) = @_;
$env->{psgix.uuid} = $self->uuid->create_str();
my $res = $self->app->($env);
$self->callback->($res);
return $res;
}
Plack::Middlewareのテスト
最後はテストについて紹介します。
Plack::Middlewareは、
use Plack::Builder;
use HTTP::Request::Common;
use Test::More;
use Plack::Test;
my $app = builder {
enable 'ETag';
sub {[
200,
['Content-Type' => 'text/plain'],
['OK']
]};
};
my $cli = sub {
my $cb = shift;
my $res = $cb->(GET '/');
is $res->code, 200;
is $res->content_type, 'text/plain';
is $res->content, 'OK';
is $res->header('ETag'),
'9ce3bd4224c8c1780db56b4125ecf3f24bf748b7';
};
test_psgi $app, $cli;
done_testing;
use Test::WWW::Mechanize::PSGI;
use Test::More;
use Plack::Builder;
my $mech = Test::WWW::Mechanize::PSGI->new(
app => builder {
enable 'ETag';
sub {[
200,
['Content-Type' => 'text/plain'],
['OK']
]};
},
);
$mech->get_ok('/');
is $mech->ct, 'text/plain';
$mech->content_is('OK');
$mech->header_is(
'ETag', '9ce3bd4224c8c1780db56b4125ecf3f24bf748b7'
);
done_testing;
Plack::Middlewareのテストは比較的パターンが決まっているので、
また、
まとめ
Plack::MiddlewareはWebサーバとアプリケーションをつなぐ層でさまざまな仕事をします。今回はそのしくみを理解するところからはじめ、
さて、
本誌最新号をチェック!
WEB+DB PRESS Vol.130
2022年8月24日発売
B5判/
定価1,628円
ISBN978-4-297-13000-8
- 特集1
イミュータブルデータモデルで始める
実践データモデリング
業務の複雑さをシンプルに表現! - 特集2
いまはじめるFlutter
iOS/Android両対応アプリを開発してみよう - 特集3
作って学ぶWeb3
ブロックチェーン、スマートコントラクト、 NFT