Perl Hackers Hub

第33回 MojoliciousでかんたんWebアプリケーション開発(3)

この記事を読むのに必要な時間:およそ 4 分

パラメータのバリデーション

MojoliciousではパラメータのバリデーションにMojolicious::Validator::Validationを利用できます。

コントローラのvalidationメソッドでインスタンスを取得でき,リクエストパラメータに対してrequiredoptionalなどの各種制約を指定します。

my $v = $c->validation;
$v->required('name')->like(qr/^[a-z]+$/);
$v->optional('group')->in(qw/admin guest/);
return $c->render if $v->has_error;

テンプレートではMojolicious::Plugin::TagHelpersの各種フィールドを使用することで,エラー時の値の復元やfield-with-errorクラスの付与を自動的に行ってくれます。

<%# TagHelpers の使用 %>
%= label_for name => 'Name (required, only a-z)'
%= text_field 'name'

<!-- エラー時に生成されるHTML -->
<label class="field-with-error" ...>...</label>
<input class="field-with-error" value="INVALID NAME" ...>

また,Mojolicious::Plugin::TagHelperscsrf_fieldと組み合わせることでCSRFCross-Site Request Forgeries対策もできます。データの更新を行うような場面では積極的に利用することをお勧めします。

<%# フォーム画面のテンプレートにcsrf_field を追加 %>
%= csrf_field

# コントローラでCSRFトークンをチェック
return $self->reply->not_found
  if $v->csrf_protect->has_error;

ユーザ定義の制約はMojolicious::Validatoradd_checkメソッドで追加します。筆者はモデルでもMojolicious::Validatorを利用するため,これを継承したリスト10のようなValidatorを作成し,次のコードをモデルに追加しています。

package MyApp::Model;
...
use MyApp::Validator;
...
sub validator {
  state $validator = MyApp::Validator->new;
}

リスト10 Mojolicious::Validatorの継承

package MyApp::Validator;
use Mojo::Base 'Mojolicious::Validator';

...

sub new {
  my ($class, @args) = @_;
  my $self = $class->SUPER::new(@args);
  # ユーザ定義の制約を追加
  $self->add_check(not_blank => sub { $_[2] =~ /^\s*$/ })
       ->add_check(not_like => sub { $_[2] =~ $_[3] });
  return $self;
}

...

MojoliciousアプリケーションではvalidatorメソッドでモデルのValidatorインスタンスを登録します。こうすることでモデルとコントローラで共通の制約を扱えます。

use Mojo::Base 'Mojolicious';
use MyApp::Model;
...
sub startup {
  my $self = shift;
  ...
  # Validator を登録
  $self->validator(MyApp::Model->validator);
  ...

アプリケーションのテスト

Mojoliciousアプリケーションは標準でサーバを通さずにリクエストを送るgetコマンドが利用できます。開発中の簡単な動作確認に重宝します。

$ ./script/my_app get /users 'ul.user-list > li' all
$ ./script/my_app get /users.json /users

本格的なテストにはMojo::Testというテスト用のモジュールを利用します。レスポンスヘッダの内容やCSSセレクタ,JSON Pointerを使用したデータの確認,WebSocketによるデータの送受信など,実際にブラウザでアクセスしたようなテストができます。リスト11Mojo::Testを利用したテストコードの一部です。

リスト11 Mojo::Testを利用したテストコード

use Mojo::Base -strict;

use Test::More;
use Test::Mojo;
...
my $t = Test::Mojo->new('MyApp');
...
subtest 'create_user' => sub {
  ...
  my $url = $t->app->url_for('create_user');

  $t->get_ok($url)
    ->status_is(200)
    ->element_exists('input[name=csrf_token]');

  my $csrf_token = $t->tx->res
                     ->dom->at('input[name=csrf_token]')
                     ->attr('value');

  subtest 'post with valid params' => sub {
    my $name = 'newuser';
    $t->post_ok($url, form => {
        csrf_token => $csrf_token,
        name => $newuser,
      })
      ->status_is(302)
      ->header_is(
        Location
         => $t->app->url_for(read_user => {name => $name});
      );
  };
  ...
};

テストの実行にはMojoliciousアプリケーションのtestコマンドを使います。Test::Harnessに付属するproveコマンドでも実行できます。

$ ./script/my_app test
$ prove -lr t

アプリケーションのデプロイ

Mojoliciousアプリケーションはさまざまな方法でデプロイできます。代表的なデプロイ方法を解説します。

ビルトインWebサーバ

Mojoliciousアプリケーションは標準でHTTPとWebSocketをサポートしたWebサーバ機能が組み込まれます。

daemonコマンドではシングルプロセスのWebサーバ,preforkコマンドではプリフォーク型のWebサーバでアプリケーションを実行できます。開発用途やアプリケーション配布後の実行コマンドとして利用します。

$ ./script/my_app daemon
$ ./script/my_app prefork
Morbo,Hypnotoad

Mojoliciousをインストールすると,mojoコマンドのほかにmorbohypnotoadコマンドもインストールされます。

morboはアプリケーションのdaemonコマンドと同じくシングルプロセスのアプリケーションサーバですが,プロジェクトのファイル変更を検知して自動でプロセスを入れ替えます。開発用のサーバとして利用します。

$ morbo script/my_app

hypnotoadはアプリケーションのpreforkコマンドと同じくプリフォーク型のアプリケーションサーバです。ホットデプロイをサポートし,無停止でアプリケーションのアップグレードができます。本番環境での利用にお勧めです。

$ hypnotoad script/my_app

コマンドの再実行だけでホットデプロイ可能
$ hypnotoad script/my_app
Starting hot deployment for Hypnotoad server 31481.

ビルトインWebサーバやmorboがコマンドラインオプションで動作を設定するのに対して,hypnotoadではMojoliciousアプリケーションのconfigで動作を設定します。Mojolicious::Plugin::ConfigMojolicuous::Plugin::JSONConfigでも同様に設定できます。

use Mojolicious::Lite;
app->config(hypnotoad => ['http://*:80']);
...
app->start;
PSGI,CGI

Mojolicious はPSGIPerl Web Server Gateway Interfaceに対応しています。plackupなどのPSGIをサポートしたコマンドにスクリプトのパスを渡すだけで,PSGIアプリケーションとして実行できます。

$ plackup -S Starman ./script/my_app

またCGIにも対応しており,そのままでCGIスクリプトとして実行できます。

ScriptAlias / /path/to/my_app/script/my_app/

まとめ

駆け足になりましたが,Mojoliciousの概要と開発するうえでの勘所を解説しました。

Mojoliciousは,本稿では解説できなかった非同期I/Oやプラグインなど,高度なWebアプリケーション開発のための機能も充実しています。ぜひとも手元で動かして,その豊富な機能を堪能してみてください。

さて,次回の執筆者は坪内佑樹さんで,テーマは「DockerによるPerlのWebアプリケーション開発」です。お楽しみに。

著者プロフィール

hayajo(Hayato Imai)

新潟で働くエンジニア。

仕事ではPerlやGoによるアプリケーション開発やインフラ設計、構築、運用に携わる。

黒い画面が大好き。