what is the best way to hanlde optional url arguments in a Catalyst controller?
For example:
I know how to match www.domain.com/foo/21
sub foo : Path('/foo') Args(1) {
my ( $self, $c, $foo_id ) = @_;
# do stuff with foo
}
But how can I match www.domain.com/foo/21 OR www.domain.com/foo/21/bar/56 ?
sub foo : <?> {
my ( $self, $c, $foo_id, $bar_id ) = @_;
# do stuff with foo, and maybe do some things with bar if present
}
Thanks
Update: Following Daxim's suggestion, I tried to use :Regex
sub foo : Regex('foo/(.+?)(?:/bar/(.+))?') {
my ( $self, $c ) = @_;
my ( $foo_id, $bar_id ) = @{ $c->req->captures };
}
But this doesn't seem to work; the url is matched, but $bar_id is always undef. If I remove the optional opperator from the end of the regex then it does capture $bar_id correctly, but then both foo and bar must be present to get a url match. I'm not sure if this is a perl regex issue, or a Catalyst issue. Any ideas?
Update:
As Daxim points out, its a regex issue. I can't see why the above regex doesn't work, but I did manage to find one that does:
sub foo : Regex('foo/([^/]+)(?:/bar/([^/]+))?') {
my ( $self, $c ) = @_;
my ( $foo_id, $bar_id ) 开发者_JAVA百科= @{ $c->req->captures };
}
(I didn't use \d+ in the captures like Daxim did as my ids might not be numeric)
Thanks all for the help and suggestions, I learnt a lot about handling urls in Catalyst :D
The Args
attribute doesn't have to be limited to a specific number of arguments. For instance, the following should work:
sub foo :Args() { # matches /foo, /foo/123, /foo/123/bar/456, /foo/123/bar/456/*
my($self, $c, $foo_id, %optional_params) = @_;
if($optional_params{bar}){
# ...
}
}
Keep in mind that all of the remaining URL segments after the path prefix and action name will be present in @remainder
. Also, since you're not specifying how many arguments you need, Catalyst will allow a URL without any args to match this action. Validate your input accordingly!
UPDATED with :Chained example
The following (untested) catalyst actions would provide you with a little more strict action matching that you seem to be looking for. The downside is that you must rely on the stash to share data between all the actions.
sub foo :Chained('/') :PathPart :CaptureArgs(1) {
my($self, $c, $foo_id) = @_;
$c->stash->{foo_id} = $foo_id; # or whatever
}
sub foo_no_bar :Chained('foo') :Args(0) {
my($self, $c) = @_;
# matches on /foo/123 but not /foo/123/bar/456
my $foo_id = $c->stash->{foo_id};
}
sub bar :Chained('foo') :PathPart :Args(1) {
my($self, $c, $bar_id) = @_;
my $foo_id = $c->stash->{foo_id};
# matches on /foo/123/bar/456 but not /foo/123 or /foo/123/baz/456
}
See item Pattern-match (:Regex and :LocalRegex)
in Catalyst::Manual::Intro#Action_types.
nick writes:
I'm not sure if this is a perl regex issue, or a Catalyst issue. Any ideas?
How about simply trying it out?
repl>>> $_ = '/foo/21/bar/56'
/foo/21/bar/56
repl>>> m|foo/(\d+)(?:/bar/(\d+))?|
$VAR1 = 21;
$VAR2 = 56;
repl>>> $_ = '/foo/21'
/foo/21
repl>>> m|foo/(\d+)(?:/bar/(\d+))?|
$VAR1 = 21;
$VAR2 = undef;
精彩评论