天然パーマです。

シンプルなWebアプリを今、自分なりに書いてみる

Webアプリでユーザーからの入力がある部分を作る際、ユーザーフレンドリーでかつ実装も簡単な方法を模索するのにkazeburoさんのブログ記事をよく参考にしていました。

CGIとは〜〜だというのはたくさん答えがあるだろうけど、自分的には「ユーザからの入力をチェックして画面にだす」ものだというのがしっくりくる。当たり前のことを書いているようだがMVCモデルでいうコントローラーの作成がCGIだと。 シンプルなWebアプリなCGIを書いてみる : blog.nomadscafe.jp

kazeburoさんはほんとにCGIで実装してたけど、フレームワークを使ったり、ライブラリを使ったりしたら、つまり、今風に、実用的に書いてみるとどうなるのかやってみています。kazeburoさんのとちょっとだけ仕様が違うがなんとなくこんなかなーという具合。あ、もちろん僕のことなんでPerlを使ってます。が、コードは非常に簡単です。

ちなみにサンプルの仕様はこんな具合。

  1. ユーザーからの入力フォームを表示、郵便番号7桁(ハイフンは入らない)を入れてもらう
  2. 郵便番号が7桁の数字かどうか、空ではないかをチェック
  3. エラーならばエラーを表示、フォームには先ほど入力した値が残っている状態
  4. 妥当なら入力された値を表示、特に保存などはしない

さての今回の実装の特徴は以下です。

  • Mojoliciousというフレームワークを使っている。今回はとりわけMojolicious::Liteというsinatra風の書き方をしています
  • 妥当性チェックにFormValidator::Liteを使っている(kazeburoさんはあえてライブラリを使ってないと思われます)。以下のソースコードではエラーメッセージをコード内に記述しているけど、外だしもできます
  • HTML::FillInForm::Liteを使っている(HTML::FillInFormより速いらしい!)。このFillInFormの機能により、エラーが起こった時にフォームに値が残ったりってのが簡単にできる

なんか、Mojolicious::LiteとかFormValidator::LiteとかHTML::FillInForm::Liteとか最近はLiteが流行りですね!

で、いよいよコードです。

zip.pl

#!/usr/bin/env perl
use Mojolicious::Lite;
use HTML::FillInForm::Lite;
use FormValidator::Lite;
use utf8;

get '/' => sub {
    my $self = shift;
    $self->stash->{error_messages} = undef;
    $self->render('index');
};

post '/' => sub {
    my $self = shift;
    my $validator = FormValidator::Lite->new( $self->req );
    $validator->set_message(
        'zip.not_null' => '郵便番号が空です',
        'zip.length' => '郵便番号が正しくありません',
        'zip.uint' => '郵便番号が正しくありません',
    );
    my $res = $validator->check(
        zip => [ 'NOT_NULL', 'UINT', [qw/LENGTH 7/] ],
    );
    my @error_messages;
    if($validator->has_error) {
        for my $message ( $validator->get_error_messages ) {
            push @error_messages, $message;
        }
        $self->stash->{error_messages} = \@error_messages;
        my $html = $self->render_partial('index')->to_string;
        $self->render_text(
            HTML::FillInForm::Lite->fill(\$html, $self->req->params),
            format => 'html'
        );
     }else{
        $self->stash->{zip} = $self->req->param('zip');
        $self->render('thankyou');
    }
 };

app->start;
__DATA__

@@ index.html.ep
% layout 'default';
<p>
  郵便番号を7桁の数字で入力してください。
</p>
% if ($error_messages) {
% for my $message (@$error_messages) {
<p><b style="color:red;"><%= $message %></b></p>
% }
% }
<form action="/" method="post">
  <input type="text" name="zip" maxlength="7" size="7"/>
  <input type="submit" value="送信" />
</form>

@@ thankyou.html.ep
<p>
 正常に入力されました。郵便番号は「<%= $zip %>」です。
 ありがとうございます。
</p>
<p><a href="/">戻る</a></p>

@@ layouts/default.html.ep
<!DOCTYPE html>
<html>
  <head><title><%= title %></title></head>
  <body><%= content %></body>
</html>

Mojolicious::Liteのアプリなんで一つのファイルで済みます。まぁちょっとでもアプリが大きくなったらファイルを切り離しますがこのくらいのサンプルを示すには楽ですね。

$ ./zip.pl daemon

とかで起動できますよ。POSTで受け取ったパラメータをFormValidator::Liteでチェックをして、エラーが無ければthankyou.html.epをレンダリング、そうでなければエラーメッセージを表示しつつindex.html.epをレンダリングしています。

kazeburoさんの言う通り、ユーザーの情報を入力してもらう=待ち構えるような機能ってWebアプリの大きな役割なのでシンプルなアプリから考察すると面白いかもしれませんね。ツッコミあればください><

CPANモジュール、ライブラリについて知りたい時は

Perl CPANモジュールガイド
冨田尚樹
ワークスコーポレーション
売り上げランキング: 26261