ローカルな正規表現(:LocalRegex)
:Pathアトリビュートでは、登録できるパス文字列は固定でしたが、もっと柔軟に正規表現で定義することもできます。正規表現で定義するためのアトリビュートは2種類あり、ネームスペースに結びつけられる「:LocalRegex」と、グローバルに定義可能な「:Regex」があります。
:LocalRegexの引数に正規表現でパス部分を定義します。例として次のようなURLを考えてみましょう。
http://<ホスト名またはIPアドレス><:Port>/foo/bar/item<数値>/comment<数値>
「item」と「comment」の後には何らかの数値が入りますが、この値は固定ではなく、さまざまな値が入るものとします。このURLにマッチするアクションは次のように定義します。
package HelloC::Controller::Foo::Bar;
# 省略
#(1)foo/bar/item<数値>/comment<数値>にマッチする定義
sub localregex_path :LocalRegex('^item(\d+)/comment(\d+)$') {
my ( $self, $c ) = @_;
#(2)itemの後の数値
my $msg = 'item:' . $c->req->captures->[0];
#(3)commentの後の数値
$msg .= ', comment:' . $c->req->captures->[1];
$c->response->body( $msg );
}
:LocalRegexアトリビュートを付加した「localregex_path」アクションは次のように定義しています。
(1)ローカルな正規表現定義
Perlで定義可能な正規表現については詳しくは説明しませんが、ここで登場する正規表現は次のような意味になります。
| 正規表現 | 意味 |
| ^item | 先頭が「item」から始まる |
| (\d+) | 1つ以上の数値が入り、グループとして保持 |
| /comment | 「/comment」という文字列にマッチ |
| $ | 末尾にマッチ |
(2)itemの後の数値を取得
正規表現のグループにマッチする値を取得するには、コンテキストのリクエストオブジェクト「$c->request」または「$c->req」の「captures」から取得します。「captures」には配列の参照として値が格納されています。「item」の後の数値を取得するには、0番目の値を取得します。
以前のCatalystでは、「$c->req->snippets」を使っている例が紹介されており、現在でも使用できます。ただし互換性のために残されているだけで、現在は非推奨となっています。
(3)commentの後の数値を取得
「item」の場合と同様に、「comment」の後の数値を取得するには、「$c->req->captures->[1]」で値を取得します。このアクションでは、itemとcommentの後の数字を表示させています。次のURLをWebブラウザで表示させた場合の例を示します。
http://<ホスト名またはIPアドレス><:Port>/foo/bar/item12/comment34
正規表現の先頭で「^」を指定していますが、これは先頭にマッチするという意味があります。この「^」を取り除くと、ネームスペース「foo/bar」と「item<数値>/comment<数値>」の間に、スラッシュ「/」を含む0個以上の文字列を含めることもできます。例えば、次のURLが呼び出された場合にもアクションにマッチさせることができるようになります。
http://<ホスト名またはIPアドレス><:Port>/foo/bar/any/path/item12/comment34
もし、ネームスペースと「item」の間の文字列(「any/path/」)を取得したい場合には、次のような正規表現を定義すれば、「captures」配列の0番目の値として取得できます。
package HelloC::Controller::Foo::Bar;
# 省略
sub localregex_path2 :LocalRegex('(.*)item(\d+)/comment(\d+)$') {
my ( $self, $c ) = @_;
my $msg = 'prefix:' . $c->req->captures->[0];
$c->response->body( $msg );
}
グローバルな正規表現(:Regex)
:Regexでも:LocalRegexと同様に正規表現でパスを定義しますが、ネームスペースに結びつけられず、グローバルにマッチさせることができます。
例として、次のようなURLを考えてみましょう。
http://<ホスト名またはIPアドレス><:Port>/user/<文字列>
ここでは、userの後にユーザー名などの文字列が入るものとします。このURLにマッチするアクションは次のように定義します。
package HelloC::Controller::Foo::Bar;
# 省略
sub regex_path :Regex('^user/(\w+)$') {
my ( $self, $c ) = @_;
my $msg = 'user:' . $c->req->captures->[0];
$c->response->body( $msg );
}
「:Regex」アトリビュートを付加した「regex_path」では、「user/」の後に1つ以上の文字列(アルファベットや数字)が入り、その文字列はグループとして保持され、「$c->req->captures」で参照できます。
このアクションでは、userの後の文字列を表示させています。次のURLをWebブラウザで表示させた場合の例を示します。
http://<ホスト名またはIPアドレス><:Port>/user/suzuki01
正規表現の先頭に「^」を指定した場合にはベースの直下に結びつけられますが、「^」を指定しない場合には、このパターンにマッチするURLであれば、ネームスペースなどに関係なくどこでもマッチさせることができます。
例えば、「:Regex('user/(\w+)$')」を指定した場合には、次のようなパスにマッチします。
| パス | 説明 |
| /user/suzuki01 | ベース直下に現れる場合 |
| /foo/bar/user/suzuki01 | 既存のネームスペース以下に現れる場合 |
| /any/path/user/suzuki01 | 存在しないネームスペース以下に現れる場合 |
| /someuser/suzuki01 | スラッシュではない文字列の後に現れる場合 |
このように:Pathや:Regexなどでネームスペースに関係ないパスを定義できますが、Catalystでは、リクエストされたパスが複数の定義にマッチする場合には、一致部分が最も長いアクションに処理が割り当てられます。


