リダイレクト
フロー制御ではアプリケーションに定義されているアクションなどを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
アクションが実行されます。