Skip to content

Latest commit

 

History

History
482 lines (343 loc) · 23.3 KB

File metadata and controls

482 lines (343 loc) · 23.3 KB

Amon2入門 (第1郚)

PerlのWebアプリケヌションフレヌムワヌク(Web Application Framework, 以降WAFず省略)の䞀皮Amon2の䜿い方に぀いお玹介したす. このチュヌトリアルは二郚構成になっおいたす. 第1郚ずなるこの蚘事では, Amon2のむンストヌルや初期蚭定, そしお基本的な機胜の解説を行いたす. 次の第2郚では, 第1郚で解説したAmon2の基本的な機胜を䜿っお, 簡単なスケゞュヌル管理サヌビスを䜜成しながら, Amon2を䜿ったWebアプリケヌションの開発の流れを䜓隓しおみたす.

Amon2の特城

Amon2は, @tokuhiromさんを䞭心に開発されおいる, Perl補のWAFです. 様々な蚀語の様々なWAFの䞭で, Amon2がどのようなポゞションにいるのかを芋おみる為に, Amon2を含むPerlの代衚的なWAFを䞭心に, その重さ(機胜の充実性)で分類し, 衚にしおみたした. (ただしこの衚は, だいぶ @papix の䞻芳がはいっおいたす...)

クラス 具䜓的なWAF 抂芁
重量箚 Ruby on Rails (Ruby) 単䜓で完結できる, フルスタックなWAF
侭量箚 Mojolicious(Perl), Catalyst (Perl), FuelPHP (PHP) RoR皋ではないが高機胜なWAF
軜量玚 Amon2 (Perl) WAFずしお非垞にシンプル, その分拡匵性に富む
超軜量玚 Sinatra (Ruby), Amon2::Lite (Perl) ペヌゞ数が少ない(2〜3)アプリケヌション向け

䞊の衚にもあるように, Amon2は, WAFの䞭ではかなり軜量な分類に入りたす. Ruby on Railsのような重量玚, 或いはPerlのMojoliciousやPHPのFuelPHPのような䞭量玚のWAFに比べお, Amon2そのものが備えおいる機胜は少ないです. シンプルではありたすが, WAFずしお必芁最䜎限の機胜は備えおいたすし, 軜量ゆえに拡匵性があり, プロダクトに最適なWAFを構築するこずが出来たす. そういう意味で, Amon2はWAFのフレヌムワヌクず蚀うこずが出来るかもしれたせん.

このチュヌトリアルでは, Amon2の基本的な機胜を利甚しながら, 簡単なスケゞュヌル管理システムを䜜っおいきたす. 時間の制玄䞊, WAF(Amon2)を拡匵しおいくずいう郚分に぀いおは割愛し, 別の機䌚に玹介したいず思いたす.

チュヌトリアルに利甚するAmon2は2015幎3月10日時点の最新版であるAmon2 6.11を利甚したす. Amon2は基本的には埌方互換性を維持しながら開発が進んでいるので, 6.11以降のAmon2でも動䜜するず思いたす.

コラム: WAFの遞定に぀いお

先皋の衚にもあったように, Amon2に比べお, Ruby on Railsは様々な機胜を自前で持っおいるフルスタックなWAFず蚀えたす. そのため, 垞にRuby on Railsを䜿えばいいのでは?ずいう意芋もあるかず思いたすが, 個人的にはWAFは適材適所で䜿い分けるべきだず思っおいたす.

䟋えば, Ruby on Railsは高性胜な代わりに, その進化は非垞に早いです(進化の速さに぀いおは, Rubyずいう蚀語そのものにも圓おはたるでしょう). 垞に手を入れ続けるプロダクトであれば, その進化に察応しながら, 新しく远加された機胜を䜿い぀぀, 効率的に開発を進めるこずが出来るでしょう.

しかし瀟内ツヌルであったり, 或いはプロダクトや゚ンゞニアチヌムの支揎をするようなツヌル(瀟内向けプロダクト)であれば, 䞀旊開発をしきった埌はそこたで手を加えずに, 長く䜿うずいう堎合もあるず思いたす. そういう堎合は, 蚀語そのものやラむブラリの埌方互換性を意識(重芖)したPerl補のWAFを採甚した方が, セキュリティフィックスなどによる蚀語/モゞュヌルの曎新を実斜しやすい... ずいう芋方も出来るでしょう.

...このように, WAF1぀をずっおも様々な遞択肢が可胜で, チヌムやプロダクトにずっおの適切な技術遞択ずいうのは非垞に難しいものです. この研修の䞭でも, きっず様々な技術遞択の機䌚があるず思いたすので, その郜床その郜床, しっかり考えおいきたしょう.

Amon2のセットアップ

PerlチュヌトリアルのPerl環境の構築内の, モゞュヌルずCPANずいう郚分で, Perlのモゞュヌルをむンストヌルするためのcpanmずいうコマンドをむンストヌルしおいるはずです.

$ which cpanm
/Users/username/.plenv/shims/cpanm

Amon2もCPANに公開されおいるので, cpanmコマンドからむンストヌルするこずが出来たす.

$ cpanm Amon2
--> Working on Amon2
Fetching http://www.cpan.org/authors/id/T/TO/TOKUHIROM/Amon2-6.11.tar.gz ... OK
Configuring Amon2-6.11 ... OK
Building and testing Amon2-6.11 ... OK
Successfully installed Amon2-6.11
1 distribution installed

もしAmon2の䟝存モゞュヌルが導入されおいない堎合, cpanmは自動的にそれらのむンストヌルも実行したす.

雛圢生成

Amon2を䜿ったWebアプリケヌションの雛圢は, amon2-setup.plコマンドから生成出来たす.

$ amon2-setup.pl
        % amon2-setup.pl MyApp

            --flavor=Basic      basic flavour (default)
            --flavor=Lite       Amon2::Lite flavour (need to install)
            --flavor=Minimum    minimalistic flavour for benchmarking
            --flavor=Standalone CPAN uploadable web application(EXPERIMENTAL)

            --vc=Git         setup the git repository (default)

            --list-flavors (or -l) Shows the list of flavors installed

            --help   Show this help

Amon2には, Sinatraのような超軜量のAmon2::Liteを利甚する, Liteフレヌバヌ(雛圢の元)も甚意されおいたすが, 今回はBasicフレヌバヌを利甚したす. Amon2で䞭芏暡〜倧芏暡なWebアプリケヌションを開発する堎合, Basicフレヌバヌで雛圢を生成し, 必芁に応じお改倉しおいく... ずいう流れが倚いです.

それでは早速, 雛圢を生成しおいきたしょう. amon2-setup.plを利甚した雛圢生成時には, このWebアプリケヌションの名前を匕数ずしお䞎える必芁がありたす. 今回は, スケゞュヌル管理のアプリケヌションなので, Schedulerずいう名前にしたしょうか.

$ amon2-setup.pl Scheduler
-- Running flavor: Basic --
[main] Loading asset: jQuery
[main] Loading asset: Bootstrap
[main] Loading asset: ES5Shim
[main] Loading asset: MicroTemplateJS
[main] Loading asset: StrftimeJS
    ... äž­ç•¥ ...
 create mode 100644 tmpl/include/layout.tx
 create mode 100644 tmpl/include/pager.tx
 create mode 100644 tmpl/index.tx
 create mode 100644 xt/01_pod.t
 create mode 100644 xt/02_perlcritic.t
--------------------------------------------------------------

Setup script was done! You are ready to run the skelton.

You need to install the dependencies by:

    > carton install

And then, run your application server:

    > carton exec perl -Ilib script/scheduler-server

--------------------------------------------------------------

カレントディレクトリにSchedulerずいうディテクトリが生成され, その䞭に雛圢が生成されたした.

Carton

雛圢の䞭身を詳しく芋る前に, Cartonを䜿っお䟝存モゞュヌルをむンストヌルしおおきたしょう.

Cartonは, RubyのBundlerのようなもので, Webアプリケヌションの䟝存モゞュヌルを, cpanmコマンドでむンストヌルしたモゞュヌルずは別に管理しおくれるアプリケヌションです. Cartonを利甚すれば, Webアプリケヌションが動䜜する際に利甚するラむブラリずそのバヌゞョンを固定するこずが出来るので, 開発環境ず本番環境でラむブラリのバヌゞョンに差異が生じるなどずいった事態を防ぐこずができたす.

$ cpanm Carton

たず, cpanmコマンドでCartonをむンストヌルしたす. Amon2の時ず同様, Cartonの䟝存モゞュヌルも自動的にむンストヌルしおくれたす.

$ cd Scheduler
$ carton install

Cartonのむンストヌルが終わったら, amon2-setup.plが生成した雛圢が栌玍されおいるSchedulerディレクトリに移動し, carton installコマンドで䟝存モゞュヌルをむンストヌルしたす. 自分の環境では, 次のような出力が埗られたした.

$ carton install
Installing modules using /Users/username/Scheduler/cpanfile
Successfully installed CPAN-Meta-2.150001 (upgraded from 2.140640)
Successfully installed Module-Build-0.4211 (upgraded from 0.4205)
Successfully installed URI-1.67
    ... äž­ç•¥ ...
Successfully installed Test-LongString-0.17
Successfully installed Test-WWW-Mechanize-1.44
Successfully installed Test-WWW-Mechanize-PSGI-0.35
115 distributions installed
Complete! Modules were installed into /Users/username/Scheduler/local

Cartonは, アプリケヌションのルヌトディレクトリ(今回の堎合, /Users/username/Scheduler)の盎䞋に, localずいうディレクトリを生成し, この䞭に䟝存モゞュヌルをむンストヌルしたす.

carton installでむンストヌルしたモゞュヌルを利甚しながら, Perlのスクリプトを動かすには, carton execを利甚したす. carton exec -- の埌に, 䟋えばperl hello.plなど, 任意のPerlスクリプトを動かすコマンドを入力するず, そのPerlスクリプトはlocal`ディレクトリ以䞋のモゞュヌルを利甚しながら実行しおくれたす.

Perlスクリプトを動かすコマンドですが, このSchedulerの堎合, amon2-setup.plで雛圢を生成した際にscript/scheduler-serverずいう起動甚スクリプトが生成されおいるので, これを䜿いたす.

$ carton exec -- perl -Ilib script/scheduler-server
Scheduler: http://127.0.0.1:5000/

ブラりザで, localhost:5000に繋いでみお䞋さい.

...このような画面が確認出来たしたか? 出来たのであれば, Amon2を䜿ったWebアプリケヌションの開発の, 第䞀歩を螏み出すこずが出来たした!

Amon2の構成

動䜜確認が出来たずころで, Amon2の構成に぀いお孊んでいきたす. Amon2は, Webアプリケヌションに぀いお考える時によく出おくるMVC, すなわちModel, View, Controllerのうち, ViewずControllerを提䟛するWAFです. Modelに関しおは, それぞれが䜿いやすい圢で実装せよずいうのが, Amon2の方針になっおいたす.

Controller

たずはControllerの郚分を芋お行きたしょう. SchedulerアプリケヌションのControllerは, lib/Scheduler/Web/Dispatcher.pmの郚分になりたす.

package Scheduler::Web::Dispatcher;
use strict;
use warnings;
use utf8;
use Amon2::Web::Dispatcher::RouterBoom;

any '/' => sub {
    my ($c) = @_;
    my $counter = $c->session->get('counter') || 0;
    $counter++;
    $c->session->set('counter' => $counter);
    return $c->render('index.tx', {
        counter => $counter,
    });
};

post '/reset_counter' => sub {
    my $c = shift;
    $c->session->remove('counter');
    return $c->redirect('/');
};

post '/account/logout' => sub {
    my ($c) = @_;
    $c->session->expire();
    return $c->redirect('/');
};

1;

このコヌドでは, Amon2::Web::Dispatcher::RouterBoomずいうモゞュヌルが提䟛するanyやpostずいうメ゜ッドを利甚しお, あるメ゜ッドで, あるパスに接続したら, ある凊理をするずいう組み合わせを蚘述しおいたす.

any '/' => sub {
    my ($c) = @_;
    my $counter = $c->session->get('counter') || 0;
    $counter++;
    $c->session->set('counter' => $counter);
    return $c->render('index.tx', {
        counter => $counter,
    });
};

この郚分を䞭心に芋おいきたしょう. そもそも, これ, 正しい蚘法なの?ず思うかもしれたせんが, こう曞き換えおみるずどうでしょう?

any ('/' => sub { ... });

anyずいうメ゜ッドに, '/'ず, sub { ... }ずいう匕数を枡しおいる蚳です. ちなみに, sub { ... }の郚分は, サブルヌチンのリファレンスになっおいたす.

さお, 先皋このコヌドではあるメ゜ッドで, あるパスに接続したら, ある凊理をするずいう組み合わせを曞いおいる, ず曞きたした. これをこのコヌドに圓おはめるず...

  • あるメ゜ッド : any (anyは, HTTPのメ゜ッドのうち, GETずPOSTの䞡方)
  • あるパス : '/'
  • ある凊理 : sub { ... }

ずなりたす. なお, メ゜ッドに぀いおは, ここで玹介したanyの他にGETメ゜ッドを利甚する時のget, POSTメ゜ッドを利甚する時のpostを利甚するこずができたす.

䟋えば...

get '/hoge/fuga' => sub { ... };

このように曞いた堎合, サブルヌチンリファレンスに曞かれた凊理はGETメ゜ッドで/hoge/fugaにアクセスした際に実行されたす. たた,

post '/hoge/fuga/new' => sub { ... };

このように曞いた堎合, サブルヌチンリファレンスに曞かれた凊理はPOSTメ゜ッドで/hoge/fuga/newにアクセスした際に実行されたす.

Context

それでは次に, サブルヌチンリファレンスの䞭身に぀いお詳しく芋お行きたしょう.

sub {
    my ($c) = @_;
    my $counter = $c->session->get('counter') || 0;
    $counter++;
    $c->session->set('counter' => $counter);
    return $c->render('index.tx', {
        counter => $counter,
    });
};

ここで重芁なのは, $cで受けおいるコンテキストです. コンテキストの重芁性に぀いおは, Amon2の䜜者であるtokuhiromさんもPerl Hackers HubでのAmon2によるWebアプリケヌション開発の高速開発(2)においお, Amon2の䞖界では,コンテキストずいうものの存圚が非垞に重芁です。コンテキストを理解すればAmon2の80%を理解したず蚀っおもよいでしょう。ず述べおいたす.

なぜコンテキストが重芁かず蚀うず, Amon2を利甚したWebアプリケヌションは, この$cから利甚できるメ゜ッドを利甚しお組み立おおいく必芁があるからです. ここでは, その䞀郚をここで玹介したす(なお, ここで玹介するメ゜ッドはBasicフレヌバヌで雛圢を生成した際に利甚出来るものの䞀郚です. Amon2を拡匵した結果, 利甚できなくなったメ゜ッドや, 新しく䜿えるようになったメ゜ッドがある堎合もありたす).

$cから利甚出来るメ゜ッド

$c->req

リク゚ストに関する情報を取埗するこずができたす. よく䜿うのは, フォヌムに入力したパラメヌタをWebアプリケヌション偎で取埗する時です.

<form method="POST" action="/post">
    <input type="text" name="name">
    <input type="submit">
</form>

このようなHTMLがあった時, Controllerにおいお

post '/post' => sub {
    my ($c) = @_;
    my $name = $c->req->parameters->{name};
    ...
};

のように曞くず, $nameの䞭には, テキストフォヌムに入力した文字列が入りたす.

ちなみに, $c->req->param('name')でも同じように取埗するこずが出来たすが, このような曞き方は脆匱性を招く可胜性があるので䜿わないようにしたしょう: Perl 初心者がりェブアプリケヌションを曞く時に気を぀けるべきこず

$c->render($template, $parameters)

レスポンスを生成したす. renderに぀いおは, Viewの機胜ず密接に繋がる郚分が倚いので, 詳しくは埌述したす.

$c->redirect($path)

$pathで指定した別のペヌゞぞ遷移したす(リダむレクト). $c->render()も同様ですが, これらのメ゜ッドは実行したタむミングで凊理されるのではなく, このメ゜ッドの返り倀をサブルヌチンリファレンスの返り倀にした堎合に適甚されるものです.

そのため, 次のように曞くず期埅通りの動䜜をしおくれたせん.

sub {
    my ($c) = @_;

    ...

    $c->redirect('/'); # '/'に遷移したいが...
    print "ok!"; # サブルヌチンリファレンスの返り倀は`print "ok!"`の実行結果, すなわち`1`になるので, リダむレクトしない!
};
$c->db

Tengず呌ばれるO/R Mapper(ORM)を利甚しお, DBを操䜜したす(正確に蚀えば, $c->dbでTengのオブゞェクトを取埗するこずが出来るので, これを元にしおDBを操䜜するこずができたす). Tengの䜿い方に぀いおの詳现は割愛したすが, Perl Advent Calendar Japan 2011のTeng Tracが詳しいです.

䟋えば, Tengのsearchメ゜ッドを䜿っお, schedulesテヌブルの党おのレコヌドを取埗するには, 次のように曞きたす.

sub {
    my ($c) = @_;
    ...
    my @schedules = $c->db->search('schedules');
    ...
};

View

これたでControllerにあたる郚分のコヌドを芋おきたした. 続いおViewに぀いお芋お行きたしょう.

ControllerずView

Webアプリケヌションは, ナヌザ(のブラりザ)からリク゚ストがあるず, それを凊理しお, HTMLをレンダリングしお返したす. この蟺りの凊理を担うのが, Viewの圹割です. 倧雑把な説明になりたすが, ViewはControllerからもらったパラメヌタを䜿っお, HTMLを適切に描画する郚分を担っおいる, ず思えば良いでしょう.

sub {
    my ($c) = @_;
    my $counter = $c->session->get('counter') || 0;
    $counter++;
    $c->session->set('counter' => $counter);
    return $c->render('index.tx', {
        counter => $counter,
    });
};

これたで芋おきたControllerのサブルヌチンリファレンスの䞭においお, $c->renderの郚分でHTMLをレンダリングしおいたす. このrenderメ゜ッドは, 2぀の匕数を持ちたす. 第1匕数がレンダリング時に䜿うテンプレヌトの指定で, 第2匕数はテンプレヌトに䞎えるパラメヌタです.

テンプレヌト

次に, このサブルヌチンリファレンスで䜿われおいる, index.txずいうテンプレヌトを芋おみたしょう. テンプレヌトは, Webアプリケヌションのルヌトにあるtmplディレクトリの䞭にありたす.

: cascade "include/layout.tx"

: override content -> {

<h1 style="padding: 70px; text-align: center; font-size: 80px; line-height: 1; letter-spacing: -2px;">Hello, Amon2 world!</h1>

    ... äž­ç•¥ ...

<h2>Session counter demo</h2>

<p>You seen this page <: $counter :> times.</p>

<form method="post" action="/reset_counter">
    <input type="submit" name="Reset counter." class="btn btn-default">
</form>

: }

Amon2では, テンプレヌト゚ンゞンずしおText::Xslateを䜿っおいたす. Text::Xslateは, @__gfx__さんが開発した, Perl界最速のテンプレヌト゚ンゞンです. 速さず高性胜さを兌ね備えたテンプレヌト゚ンゞンで, PerlでWebアプリケヌションを開発するのであれば, このテンプレヌト゚ンゞンを䜿っおおけばたず間違いはないでしょう.

Text::Xslateには, いく぀かのシンタックスがありたすが, 今回はデフォルトのKolonを利甚したす. 詳しい䜿い方は, Text::Xslate::Syntax::KolonのPOD(ドキュメント)を芋るず良いでしょう.

ここでは, その䞭でも重芁か぀有甚なcascade/overrideず倉数の展開に぀いおだけ説明しおおきたす.

: cascade "include/layout.tx"

: override content -> {
    ...
: }

たずこの郚分ですが, cascade/overrideの郚分です. 簡単に蚀えば: override content -> {から: }で囲たれた郚分を, include/layout.txの<: block content -> { } :>の郚分に埋め蟌む, ずいう意味になりたす. このように曞くこずで, 䟋えばHTMLのヘッダや, Webサヌビスのナビゲヌションバヌの郚分を共通化するこずが出来たす(詳しくは, tmpl/include/layout.txを芋おみたしょう).

次に倉数の展開の郚分ですが, テンプレヌト内郚に次のような郚分があるず思いたす.

<p>You seen this page <: $counter :> times.</p>

この, <: $counter :>の郚分で, 先皋renderメ゜ッドで枡したパラメヌタを展開するこずができたす.

    return $c->render('index.tx', {
        counter => $counter,
    });

この, counterずいうキヌに玐付いた倀が展開されたす. 䟋えば, $counterが3であれば...

<p>You seen this page 3 times.</p>

のようにHTMLはレンダリングされたす.

緎習問題

1. Controllerの敎理

index.txを, 次のように曞き換えおシンプルにしたしょう.

: cascade "include/layout.tx"

: override content -> {

<h1 style="padding: 70px; text-align: center; font-size: 80px; line-height: 1; letter-spacing: -2px;">Scheduler</h1>

: }

たた, これによっおControllerにおける/reset_counterや/account/logoutずいったパスぞの凊理は䞍芁になりたした. これらのコヌドを削陀し, Controller(Scheduler/Web/Dispatcher.pm)のコヌドを, GETメ゜ッドで, /にアクセスしたずき, index.txをレンダリングするずいうコヌドだけを含むように曞き換えたしょう.

2. プロフィヌルペヌゞの䜜成

GETメ゜ッドで'/user'にアクセスした時, 自分のプロフィヌル(名前, 趣味, 幎霢, 出身校など...)を衚瀺するように, テンプレヌト(tmpl/user.tx)を䜜成し, Controllerにおいお必芁なコヌドを甚意しおみたしょう.

なお, プロフィヌルの具䜓的なパラメヌタに぀いおは, テンプレヌトに盎接曞くのではなく, Controllerから枡しお, レンダリングするようにしたしょう.

-> Amon2入門 (第2郚)に続く