Webtech Walker

Ark(Catalyst)のルーティング

今日社内勉強会でtypesterからArkのルーティングについてお話があって、なんとなくしかわかってなかったのがだいぶすっきりしたので自分なりのまとめ。この辺はCatalystと同じなはずなのでCatalystでも一緒だと思われ。

ルーティングで使用するアトリビュートには大きく分けて下記の3つ。

  • Path
  • Regex
  • Chained

Path

PathアトリビュートにマッチさせたいURLを記述する。これが基本。

パッケージ名がFooの場合はfooからのパスになる。これは/foo/barにマッチ。

package SampleApp::Controller::Foo;
use Ark 'Controller';

# /foo/bar
sub bar :Path('bar') {
}

メソッド名は関係ない。下記も/foo/barにマッチする。

package SampleApp::Controller::Foo;
use Ark 'Controller';

# /foo/bar
sub hoge :Path('bar') {
}

Pathに何も指定しないとそのpackageのルートになる。これは/fooにマッチ

package SampleApp::Controller::Foo;
use Ark 'Controller';

# /foo
sub index :Path {
}

Rootコントローラーを書く場合は下記の様にnamespaceを設定する。これやらないとRootコントローラーが/rootのURLにマッチしちゃう。

has '+namespace' => default => '';

これは/aboutにマッチする。

package SampleApp::Controller::Root;
use Ark 'Controller';

has '+namespace' => default => '';

# /about
sub about :Path('about') {
}

これは/(トップ)にマッチする。

package SampleApp::Controller::Root;
use Ark 'Controller';

has '+namespace' => default => '';

# /
sub index :Path {
}

Pathの中を絶対パスで書くと絶対パスでマッチするのでpackage名も関係なくなる。これは/hoge/fugaにマッチする。

package SampleApp::Controller::Foo;
use Ark 'Controller';

# /hoge/fuga
sub bar :Path('/hoge/fuga') {
}

Args

ArgsでURLからパラメータを取得できる。Argsに受け取る数を指定する。

これは/foo/*にマッチ。

package SampleApp::Controller::Foo;
use Ark 'Controller';

# /foo/*
sub index :Path :Args(1) {
}

下記の例では/と//*にマッチして/はindex1、//はindex2のメソッドを実行する。

package SampleApp::Controller:Root;
use Ark 'Controller';

has '+namespace' => default => '';

# /*
sub index1 :Path :Args(1) {
}

# /*/*
sub index2 :Path :Args(2) {
}

Argsのパラメータを受け取る場合はこんなかんじ。

my ($self, $c, $args) = @_;

でURLに含まれているパラメータを受け取れる。Args(2)の場合は

my ($self, $c, $args1, $args2) = @_;

でおk。

/aboutと/*があった場合は/aboutが優先される。

package SampleApp::Controller:Root;
use Ark 'Controller';

has '+namespace' => default => '';

# /*
sub default :Path :Args(1) {
}

# /about => こっちが優先される。
sub about :Path('about') {
}

Argsに何も指定しないと////…みたいに全部にマッチするのでRootコンローラーにこれを置いて404とかに使う。

package SampleApp::Controller:Root;
use Ark 'Controller';

has '+namespace' => default => '';

sub default :Path :Args {
    # 404
}

Local

Pathにメソッド名を指定したのと同じになる。

sub foo :Path('about') {
}

sub about :Local {
}

は同じ。

Global

Pathにメソッド名を絶対パスで指定したのと同じになる。

sub foo :Path('/about') {
}

sub about :Global {
}

は同じ。

Regex

正規表現でマッチする。パッケージ名とかメソッド名は関係ない。

sub hoge :Regex('^/article/(\d{4})/(\d{2})/(\d{2})') {
}

これでRegexでしたURLにマッチする。()の中の値は下記のようにして受け取れる。

my ($self, $c, $year, $month, $day) = @_;

これは簡単。

Chained

/user/{username}/blog とか /wiki/{pagename}/edit みたいな複雑なURLを使いたい場合はChainedを使う。これが一番難しい。正直いまいちよくわかってないのは否めない。

/user/{username}/blog、/user/{username}/profile みたいなURLにマッチさせたいときは以下の様に書く。

sub user :Chained('/') :PathPart :CaptureArgs(1) {
    my ($self, $c, $username) = @_;
    $c->stash->{username} = $username;
}

# /user/{username}/blog
sub user_blog :Chained('user') :PathPart('blog') {
}

# /user/{username}/profile
sub user_profile :Chained('user') :PathPart('profile') {
}

最初のChainedは(’/')になって、そこから派生するメソッド(ここではuserblogとuserprofile)をつくって、元になるメソッド(user)をChainedで指定、PathPartでそれに続くパスを指定する。PathPartの指定がない場合はメソッド名がPathPartに値になる。

ただし/user/{username}というURLにuserメソッドがマッチするわけではない。

Argsを指定したりすることもできる。

sub user :Chained('/') :PathPart :CaptureArgs(1) {
    my ($self, $c, $username) = @_;
    $c->stash->{username} = $username;
}

# /user/{username}/profile/*
sub user_profile :Chained('user') :PathPart('profile') :Args(1) {
}

CaptureArgs(2)とかすると2つの値を受け取れる。

sub user :Chained('/') :PathPart :CaptureArgs(2) {
    my ($self, $c, $username, $foo) = @_;
    $c->stash->{username} = $username;
    $c->stash->{foo} = $foo;
}

# /user/{username}/{foo}/profile
sub user_profile :Chained('user') :PathPart('profile') {
}
このエントリーをはてなブックマークに追加