When taking multiple URL arguments, order matters
Closed this issue · 5 comments
Hello,
I have been working with your framework for a few days now and have created a few API routes. I am admittedly not very good with regex however I have managed to drag my way through. Right now I have an API I am setting up which has two routes, one which is http://test/list/:university and the other is http://test/list/:university/:class, where in both university and class are variables. I have these two routes setup:
getApi()->get('/list/(.+)/(.+)', 'listSemester', EpiApi::external);
getApi()->get('/list/(.+)', 'listClasses', EpiApi::external);
and they work in this order, however if I put
getApi()->get('/list/(.+)', 'listClasses', EpiApi::external);
getApi()->get('/list/(.+)/(.+)', 'listSemester', EpiApi::external);
then the first expression takes both url arguments instead of just taking the /list/:university
Is this just me misusing regex?
The correct solution is to be a bit more explicit about your regular expression.
getApi()->get('/list/([^/]+)', 'listClasses', EpiApi::external);
getApi()->get('/list/([^/]+)/(.+)', 'listSemester', EpiApi::external);
In a regex the .
means any character (including /
). So /universityname/classname
actually matches (.+)
because it's saying one or more of any character. Doing (.+)/(.+)
matches both because the /
in between terminates the first grouping and it starts looking for the second sub pattern.
([^/]+)
means match one or more of any character that is not a /
. and should solve your problem as long as :university
and :class
does not contain a /
.
Thank you very much for your help on this, it has got me quite far, I hope you don't mind one more question, if you don't mind telling me what about escaped strings? If I escape my variables in a string with a forward slash, is there any possible way to match so it will take // but not / Sorry to bother you again.
Can you provide an example? Not sure I'm understanding.
FYI, you can always pass and access GET parameters as normal.
/foo/(.+)?bar=value
With the callback function function
function cb($param) {
echo "{$param} comes from the path";
echo "{$_GET['bar']} should equal value";
}
Okay I have been doing some more research and have realized I may be going about this the wrong way. Here is an example of what I am attempting:
I have the following line in my code:
getApi()->get('/search/([^/]+)/([^/]+)', array('Search','classNotes'), EpiApi::external);
and I am hitting the following url:
http://localhost/search/university/class
And it works fine, however I if I try to pass a url in the form of
http://localhost/search/university/class%2Fwithslash
I end up with an object not found error.
Next I tried http://localhost/search/university/class%252Fwithslash
and this caused apache to parse this as a / and look for a route something like
get('/search/([^/]+)/([^/]+)/([^/]+)
which takes different arguments completely
I also tried
http://localhost/search/university/class\/withslash
But the regex took the slash and ran with it as a place to split the expression.
Basically I want to be able to accept slashes in the without having to change the format of the API, this whole question more or less was summed up best by you, when you said in your first reply that this works as long as it does not contain a slash, and now I'm trying to figure out the best way to be able to take a slash.
Thank you for your patience with me.
If you know that the class is the last parameter then you can do the following.
getApi()->get('/search/([^/]+)/(.+)', array('Search','classNotes'), EpiApi::external);
^^^^
But this means every URL under /search/{anything_without_a_slash}/{everything_else}
will match this route.