リダイレクト
フロー制御ではアプリケーションに定義されているアクションなどを1回のリクエスト内で呼び出しますが、リクエストされたURLとは別のURLにリダイレクト(転送)させたい場合、例えばデータベースの項目削除後に一覧表示するURLに移動させる場合には、Catalyst::Responseに定義されているredirectメソッドを使用します。
redirectメソッドは、引数として、リダイレクト先のURLとHTTPステータスコード(3xx)の2つを指定します。ステータスコードは省略可能であり、省略時には302(Found)が設定されたものとして動作します。
redirectメソッドは次のように使用します。
# 一時的なURLにリダイレクト
$c->res->redirect('http://example.com/temp/');
# ステータスコード301(Moved Permanently)を指定してリダイレクト
$c->res->redirect('http://new.example.com/', 301);
# アクションに対応するURIへリダイレクト
$c->res->redirect($c->uri_for('some_action'));
ステータスコード301は、リクエストされたリソースが恒久的に移動されたことを意味します。例えばサイト移転後に古いURL「http://old.example.com/」にリクエストされた場合に、新しいURL「http://new.example.com/」へリダイレクトさせる際に使用します。
ステータスコード302は、リクエストされたリソースが一時的に移動されていることを意味します。しかし掲示板などでPOSTで送信された後に、GETで一覧表示用URLに移動させるために使われるなどの、誤った使い方(もともとの仕様ではメソッドを変更してはならない)が多かったため、現在ではこのようなリダイレクトを行う場合には303(See Other)を使うべきです。
Chainedアクション
最近のWebアプリケーションでは、リクエストパラメータとして値を渡すのではなく、URLのパスに値を含めた形式を採用する事例を多く見かけるようになってきています。
例えば、IDが12である商品の34番目のコメントを表示する場合について、リクエストパラメータを使用した場合だと次のようになると思います。
http://<ホスト名またはIPアドレス><:Port>/comment?itemid=12&commentid=34
これをパスに含めたURLに直すと次のようになります。
http://<ホスト名またはIPアドレス><:Port>/item/12/comment/34
このようなURL(※注1)に対応するアクションは、:Regexアトリビュートを使用して定義することもできますが、コメントの編集や削除などのアクションを考えると、商品情報の取得やコメントの取得などの共通する部分などの似たような処理を繰り返し書く必要があり、メンテナンス性や可読性を下げる原因となります。
Catalystでは、このような場合にChainedアクションを使用することで、パスの一部分に応じた処理を別々のアクションとして定義し、それらを連鎖(Chain)させることですっきりとしたコードを書くことができるようになっています。
REST(Representational State Transfer)fulなURLと呼ばれます
Chainedアクションで使用するアトリビュート
Chainedアクションでは、Chainedアトリビュートとともに、いくつかのアトリビュートを補助的に使用します。Chainedアクションで使用するアトリビュートには次のようなものがあります。
| アトリビュート | 説明 |
| PathPart | URLに現れる文字列を指定 |
| Chained | 対象アクションの前に呼び出されるアクションを指定 |
| CaptureArgs | 取り得る引数の数を指定。省略された場合にはエンドポイントとなる。 |
| Args | エンドポイントで取り得る引数の数を指定 |
| PathPrefix | PathPartの代わりにクラスのprefixを指定 |
| ChainedParent | 上位のコントローラの同名アクションに連鎖させるために指定 |
Chained/PathPart
Chainedアクションを定義する場合の基本的な形は、「:Chained :PathPart(:CatpureArgsまたは:Args)」となります。
Chainedアトリビュートには上位となるアクション名を指定し、PathPartアトリビュートには、対象とするChainedアクションのパス文字列を指定します。
URLの最上位となるChainedアクションには、Chainedアトリビュートの引数として「/」を指定します。:Chainedのように引数を持たない場合には、:Chained('/')を指定した場合と同じ意味になります。
次の例では、「/foo/bar/baz」というURLにマッチするChainedアクションを定義しています。
package MyApp::Controller::ChainedSample;
# 省略
# 最上位のアクションで、パス文字列はfooとなる
sub foo_action :Chained('/') :PathPart('foo') :CaptureArgs(0) {
# 省略
}
# 上位のfoo_actionアクションを指定し、パス文字列はbarとなる
sub bar_action :Chained('foo_action') :PathPart('bar') :CaptureArgs(0) {
# 省略
}
# 上位のbar_actionアクションを指定し、パス文字列はbazとなる
sub baz :Chained('bar_action') :PathPart :Args(0) {
# 省略
}
ここでは末端のbazアクションから、その上位アクションであるbar_actionアクション、bar_actionアクションから最上位のfoo_actionアクションを指定することで、連鎖するアクションを定義しています。ここで「/foo/bar/baz」のURLが呼び出された場合には、foo_action、bar_action、bazアクションの順に実行されます。
Chainedアクションを定義するには、必ず最上位アクションにたどり着くようにする必要があることに注意してください。
bazアクションではPathPartアトリビュートの引数を省略していますが、このように:PathPartと指定した場合や、:PathPart()と指定した場合、さらに:PathPart自体を省略した場合には、デフォルトの動作としてアクション名(サブルーチン名)が部分パス文字列に指定されたものとして動作します。
CaptureArgs/Args
CaptureArgsとArgsアトリビュートでは、対象となるアクションが取り得る引数の数を指定します。
それぞれの違いは、対象とするアクションをエンドポイント(末端のアクション)とするかどうかになります。CaptureArgsを使用した場合にはエンドポイントにはならず、Argsアトリビュートを指定した場合、またはCaptureArgs/Argsのどちらも指定しない場合にはエンドポイントになります。またCapturesArgs/Argsのどちらも指定しない場合には、取り得る引数の数は不定(0以上)となります。
ここで、次のようなChainedアクションを考えてみましょう。
package MyApp::Controller::ChainedSample;
# 省略
# 引数を1つだけ受け取る
sub capture_user :Chained('/') :PathPart('user') :CaptureArgs(1) {
# 引数の値を受け取る
my ( $self, $c, $userid ) = @_;
# 省略
}
# CaptureArgs/Argsを省略しているのでエンドポイントとなる
sub address :Chained('capture_user') :PathPart('address') {
# 省略
}
# 引数を1つも受け取らないエンドポイント
sub profile :Chained('capture_user') :PathPart('profile') :Args(0) {
# 省略
}
# 引数を1つだけ受け取るエンドポイント
sub comment :Chained('capture_user') :PathPart('comment') :Args(1) {
# 引数の値を受け取る
my ( $self, $c, $commentid ) = @_;
# 省略
}
# このアクションはどこからも呼び出されない
sub capture_blog :Chained('/') :PathPart('blog') :CaptureArgs(1) {
# 省略
}
このコードで構成されるパスは次のようになります。
| パス | エンドポイント | エンドポイントで取り得る引数の数 |
| /user/*/address/... | address | 不定(0以上) |
| /user/*/profile | profile | 0 |
| /user/*/comment | comment | 1 |
Catalystでは、エンドポイントからChainedアトリビュートの引数をたどりながら最上位まで到達したものだけをパスに割り当てます。よってcapture_blogアクションはエンドポイントからまったく参照されていないため、呼び出されることはありません。
また、上記のコードでは指定された引数を@_から取得していますが、それ以外の方法としてCatalyst::Requestのcaptures、およびargumentsから取得することもできます。
PathPrefix
PathPrefixアトリビュートは、コントローラのネームスペースをパス文字列とする場合に指定します。PathPrefixはPathPartの代わりとして指定するものなので、両方を同時に指定することはできません。
次の例では、MyApp::Controller::Foo::BarコントローラのbazアクションのURLを、ネームスペースである「/foo/bar」として指定しています。
package MyApp::Controller::Foo::Bar;
# 省略
# bazアクションのURLはネームスペース「/foo/bar」となる
sub baz :Chained('/') :PathPrefix {
# 省略
}
ChainedParent
ChainedParentアトリビュートは、親階層のコントローラに対して、同じ名前のアクションに連鎖させる場合に使用します。
ここで、MyApp::Controller::Userコントローラと、その子階層のコントローラであるMyApp::Controller::User::PCとMyApp::Controller::User::Mobileに同じ名前のprofileアクションを定義し、それぞれの子階層コントローラのprofileアクションにChainedParentアトリビュートを設定した場合について考えてみます。
package MyApp::Controller::User;
sub profile :Chained('/') :PathPart('profile') :CaptureArgs(1) {
#省略
}
package MyApp::Controller::User::PC;
sub profile :ChainedParent :PathPart('pc') :Args(0) {
#省略
}
package MyApp::Controller::User::Mobile;
sub profile :ChainedParent :PathPart('mobile') :Args(0) {
#省略
}
上記のように定義すると、「/profile/*/pc」というURLが呼び出された場合には、User、User::PCの順にprofileアクションが実行され、「/profile/*/mobile」というURLが呼び出された場合には、User、User::Mobileの順にprofileアクションが実行されます。
