天然パーマです。

PerlのWAFはMojolicious推しな件とそのノウハウ

Perlerな皆さん!WAFは何を使っていますかー!? 昨日行われたという「Perl Beginners #1」のレポートを漁っていると @hsksyuskさんのエントリにこう書かれていましたので僕もつい乗っかってみたくなりました。

PerlのWAFは何がいいの?
  • @ytnobodyさんはKossy推し。
  • 僕はAmon2推し。

ぼ、ぼ、ぼ、僕はMojolicious推し!!!

ということでなぜ僕が今、Mojolicious推しなのかを探るためにその「いい点」と思っていること列挙してみました。 それと同時に悪い点も見えてきて、かつバッドノウハウもあるなーって感じなのでまとめてみます。



Mojoliciousのいい点

依存モジュールが無くポータブル

ご存知の通りMojoliciousは標準で入っているモジュールに依存しているだけで、 残りは自前で実装しています。なので、

$ cpanm Mojolicious

とすれば外部のモジュールをインストールしなくとも入るので非常にポータブル! 以前Catalystを使っていて、Catalystが依存しているMooseのバージョンが問題になって、 アプリが動かなくなったりCatalyst自身のアップデートができないことがありましたが、 そういうことは起こらないですね。 最低限のアプリならMojolicious自体だけのバージョンだけを気にしていればよいです (とはいえモデル層で他のモジュールに依存しがちですが!)。

そこそこでかいアプリも書ける

MojoliciousというとSinatraライクな「Mojolicious::Lite」が有名で、 一つのファイルでコントローラもビューも書けて簡単だぜ!というのがPRとして売りになってる気がします。 Mojolicious::Liteのアプリを作るための雛形は、

$ mojo generate lite_app hoge.pl

とかして作ります。さてLiteで作ったアプリがどんどん大きくなっていくとしましょう。 hoge.plと同じ階層に「templates」というディレクトリを作って「index.html.ep」などのテンプレートファイルを置けば、 hoge.pl内のデータセクションに書いていたビューを切り離すことができます。 ただ、コントローラとディスパッチャ等を分割したモジュールで管理したいという時には、 Liteでは対応できません。そこでコマンドを打ち込みます。

$ mojo generate app Hoge

すると以下の構造で「普通の」Mojoliciousアプリの雛形を作ってくれます。

./
├── lib
│   ├── Hoge
│   │   └── Example.pm
│   └── Hoge.pm
├── log
├── public
│   └── index.html
├── script
│   └── hoge
├── t
│   └── basic.t
└── templates
    ├── example
    │   └── welcome.html.ep
    └── layouts
        └── default.html.ep

「Mojolicious」でググると「Mojolicious::Lite」の話題ばかり出てくるのですが、 僕はこちらの「普通の」Mojoliciousアプリを使うケースが多いですし、そこそこの大きさのアプリにも耐えられます。

Mojo::Templateが柔軟

Text::MicroTemplateの元になったというMojo::TemplateがPerlそのまま書けるので、一人でやる分には柔軟でよいです。 気に入っているのはテンプレート内で使える「layout」という関数。「templates/layout/default.html.ep」がこのようにあったとして

<html>
  <body>
    <%= content %>
  </body>
</html>

「templates/root/index.html.ep」でこのレイアウトを使うには冒頭にでもlayout関数を使います。

% layout 'default';

<h1>Hello Hoge</h1>

レイアウトを変えることもテンプレートファイル内で容易にできるのがいいのです。

$selfだけ

コントローラではコンテキストオブジェクトを扱わずに「$self」だけでごにょれるのがいいですね。

sub index {
    my $self = shift;
    $self->stash->{message} = 'Hage';
    $self->render('message');
}

VCだけ

Mojoliciousはモデル部分は後述するアクセサ的の提供のみを行ってビューとコントローラのみを提供しています。 まぁ最近の薄いWAFはだいたいそうですが、簡素でよいですね。

Mの登録、ディスパッチの記述

さて、モデルの登録には遅延させるために上記の例だと「Hoge.pm」にこうやって記述します。

package Hoge;
use Mojo::Base 'Mojolicious';
use Hoge::API;

sub startup {
    my $self = shift;
    $self->attr( api => sub { Hoge::API->new() } );
}

至って簡単です。またディスパッチの記述も

my $r = $self->routes;
    $r->namespace('Hoge::Controller');
    $r->get('/')->to( "root#index");

なんてできて楽でかつ柔軟ですね。

CGIで動かす時にLiteが便利

たまーに面倒くさい感じでさくらの一番安いレンタルサーバーでフォームだけの処理のCGIを書くとかあるんですが、 そういう時に依存がないMojoliciousのLiteを使ってCGIにしちゃうのは結構便利です。 Liteのアプリを「hoge.cgi」などの拡張子にすればそのままApache経由のCGIとして動いたりしますからね。

ドキュメントがしっかりしている

さすがにMojoliciousはドキュメントがしっかりしています。 だいたい「Mojolicious::Controller」を見ることが多いですね。

公式のWebページや開発モードNot Foundページがお洒落

公式のWebページがよくデザインされてかっこかわいいです。また、 アプリのデフォルトのNot Foundページなども凝っていて、 最新版のMojoliciousを使っていて、かつ開発モードで立ち上げてるとこのようにルーティングパスまで表示してくれて洒落てます。

開発モードのNot Found

開発陣が複数人いる、開発が活発

開発陣がSebastian Riedelがリーダーになって複数人いて開発が活発です。



Mojoliciousのイケてない点

さて、Mojoliciousのよい点ばかりを挙げてきましたが個人的に思うイケてない部分もあります。

5.10以上必須

Perl 5.10以上が必須でかつ5.12が推奨されています。 5.8の環境って今ではあまりないですが、昔の環境でどうしても動かしたいって時に、 せっかくのポータブルなコンセプトのMojoliciousが入らないと萎えます。

セッションの扱い

デフォルトのセッションは非常に貧弱で、暗号化した文字列をcookieに入れるだけのサポートです。 なのでMojoliciousの機能は使わずに、 よくPlack::Middleware::SessionとPlack::Sessionを組み合わせたセッション管理を行うことが多いです。 アプリ用の.psgiを書いて

my $psgi = Mojo::Server::PSGI->new( app_class => 'Hage' );
my $app = sub { $psgi->run(@_) };

builder {
    enable 'Session', store => 'File';
    $app;
};

とします。べたにやるならコントローラの中で「use Plack::Session;」して

sub index {
    my $self = shift;
    my $session = Plack::Session->new( $self->req->env );
    my $access_token = $session->get('access_token');
    my $access_secret = $session->get('access_secret');
    ...;
}

などとしてセッションの値を取得します。まぁ.psgiは毎回書いてそれを開発、本番用に使っているので、 Plackのミドルウェアを使うってのはいいんですけどね。

redirect_toがたぶん相対パスを表す

こういう細かいちょっとした変なところがあるのでたまにストレスが溜まります。

$self->redirect_to('/hage');

とすると302でリダレイクトしてくれるのですが、これがアプリ内の「/hage」の絶対パスじゃなくて相対パスなんで、 本番環境だとおかしくなったりします。

$self->redirect_to( $self->req->url->base . '/hage' );

とか長ったらしい記述をして回避しがちっす。 他にも軽くはまるところがいくつかあった気がします。

*ここについて追記*

id:kitsさんが検証してくれた通り、 ローカルの環境だとうまくリダイレクトされるんですが、なんか本番のReverseProxy+ホスト名だとうまくいかないケースがあったんですよね>< ちょっと今、再現できないのですがLocationヘッダの中身がおかしかった気がするす。



まとめ

Mojoliciousを個人的に推している理由を解説してきましたが、 ほんとWAFは何を使ってもいいと思います。 最近はAmon2とかKossyとか!薄くて使い勝手がよいWAFがたくさんありますしね。 でも僕はMojolicious推しですよーというエントリでした。

お知らせ

前号Perlのコードが出てきたメルマガ「ゆーすけべーラジオ」は、 次回28日(火)に発行です。よろしければチェックしてください。