views:

282

answers:

8

I want to match /foo, but not /foo/ (where foo can be any string) or /

I tried a lot of things along these lines:

sub match :Path :Regex('^[a-z]+$')
sub match :Regex('^[a-z]+$')
sub match :Path :Args(1)

But I cannot achieve what I need.

I don't believe the problem is with my regex, but because I want to handle a path without an argument:

For example, for /aab/c, I get from Catalyst:

[debug] "GET" request for "aab/c" from "192.168.1.100"
[debug] Path is "aab"
[debug] Arguments are "c"
A: 

I think this the regex you are looking for:

^/[^/]+$

[^/] will match anything that is not a slash. So there you will match anything starting with a slash, followed by one or more characters, none of them slashes.

pablobm
match :Regex("^/[^/]+$") doesn't match any thingmatch :Path :Regex("^/[^/]+$") matches /foo, /foo/ and /foo/a
Julien
A: 

I don't know Catalyst, but based on reading the docs it looks like you want

sub match :Regex('^[a-zA-Z]*$') {}

The regex breaks down like this:

  • ^ match just after the root (i.e. the /)
  • [a-zA-Z]* matches zero or more a-z or A-Z characters
  • $ match the end of the url
Chas. Owens
That's pretty much the same regex written in a weird way :-)
Julien
+1  A: 

Disclaimer: I'm not familiar with Catalyst; these are (Perl) regexes.

If "foo" must be alphanumeric this will do what you want:

^\/\w*$

If "foo" can truly be anything, use this instead:

^\/$|^\/.*[^\/]$
Michael Carman
+2  A: 

According to the docs,

Regex matches act globally, i.e. without reference to the namespace from which it is called...

If you want the regex to match only at the root of the current namespace, try using a LocalRegex action instead:

sub match : LocalRegex('^[a-z]+$')
Alan Moore
+2  A: 

Regex or LocalRegex will only match if there is no other more specific action that could handle the request.

You need to support sub lowermatch :Regex ('/([a-z]+)/([a-z]+)') { } to catch /foo/bar and handle it appropriately. Your existing stub shouldn't be catching '/' though. That should be going through to Root->default

Alternatively you can $c->forward in your existing sub if there are no arguments, ie:

sub match :Regex ('/foo') {
    my ($self, $c, @args) = @_;
    $c->forward('noargs_action') unless @args;
    ...
}

Hope that helps.

RET
+1  A: 

LocalRegex actions are probably OK for what you describe, but you're fighting against the HTTP spec if you're doing this kind of thing.

singingfish
+2  A: 

No :Args specified will mean 'any number of arguments, 0 or greater'. This means that in your initial examples, /foo/bar and /foo/bar/baz are matches. Path elements after the regex that the regex itself doesn't match will get eaten as arguments to the action.

(People who are telling you things about your regexp and saying 'I don't know Catalyst but' are missing the point here that $ in a regexp matcher can match just before a '/', and not always at the end of the URL, in Catalyst. The rest is then used as arguments.)

You will require :Args(0) for what you're trying to achieve, specifying that 0 path parts are to be used as arguments (so /foo/bar doesn't match).

However, a quick test using

sub match :LocalRegex('^[a-z]+$') :Args(0)

demonstrates that /foo and /foo/ both match, (but this is a step closer: /match/xyz doesn't). Catalyst treats '/' specially in paths, so I'm not sure if you can do better than this.

ijw
A: 

This answer might not be what you're looking for, but I just want to put it in here for completeness sake: I would probably use something like ModRewrite to make a redirect from the /foo/ to the /foo variant.

phaylon