This repository has been archived by the owner. It is now read-only.

Routing does not handle trailing slashes consistently #149

Closed
AD7six opened this Issue Aug 15, 2011 · 53 comments

Comments

Projects
None yet
@AD7six
Contributor

AD7six commented Aug 15, 2011

This may be merely something to add to the docs.

Using a simple application such as the following:

<?php

require_once __DIR__.'/../silex.phar';
use Symfony\Component\HttpFoundation\Response;

$app = new Silex\Application();

$app->get('/', function () use ($app) {
      return new Response('Home root');
});
$app->get('/hello', function () use ($app) {
      return new Response('No trailing slash');
});
$app->get('/hello/', function () use ($app) {
      return new Response('With trailing slash');
});

$app->run();

this is the observed behavior:

However expected behaviour would be that the last route is inaccessible:

If the last route is not defined, the result of requesting /hello/ is "Sorry, the page you are looking for could not be found." - but you'd expect it to match the routefor /hello.

This is testing on an arch linux machine running nginx - though that shouldn't really be much of a factor.

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Aug 15, 2011

If the last route is not defined, the result of requesting /hello/ is 
"Sorry, the page you are looking for could not be found." - but 
you'd expect it to match the routefor /hello.

The opposite is true: /hello may mean /hello/ (but not the other way around).

This is exactly how Apache and Symfony2 handle trailing slashes.

Silex should work the same way as it is based on Symfony2 components.

ghost commented Aug 15, 2011

If the last route is not defined, the result of requesting /hello/ is 
"Sorry, the page you are looking for could not be found." - but 
you'd expect it to match the routefor /hello.

The opposite is true: /hello may mean /hello/ (but not the other way around).

This is exactly how Apache and Symfony2 handle trailing slashes.

Silex should work the same way as it is based on Symfony2 components.

@stof

This comment has been minimized.

Show comment
Hide comment
@stof

stof Aug 15, 2011

Contributor

And Symfony matches the route with the trailing slash when it is omitted only when there is no route without the trailing slash

Contributor

stof commented Aug 15, 2011

And Symfony matches the route with the trailing slash when it is omitted only when there is no route without the trailing slash

@AD7six

This comment has been minimized.

Show comment
Hide comment
@AD7six

AD7six Aug 16, 2011

Contributor

Perhaps either of you could explain the following then:

Silex doesn't match /hello/ to anything raising a 404

Contributor

AD7six commented Aug 16, 2011

Perhaps either of you could explain the following then:

Silex doesn't match /hello/ to anything raising a 404

@stof

This comment has been minimized.

Show comment
Hide comment
@stof

stof Aug 16, 2011

Contributor

This is normal, as explained above: a route defined with a trailing slash will also match the url without the / when there is no route for this one (which is the same behavior than Apache as explained above). In your case, you are trying to do the opposite. A route defined without the trailing slash will not match the url with a trailing slash.

Contributor

stof commented Aug 16, 2011

This is normal, as explained above: a route defined with a trailing slash will also match the url without the / when there is no route for this one (which is the same behavior than Apache as explained above). In your case, you are trying to do the opposite. A route defined without the trailing slash will not match the url with a trailing slash.

@AD7six

This comment has been minimized.

Show comment
Hide comment
@AD7six

AD7six Aug 16, 2011

Contributor

Silex-project.org contains this snippet on the first page:

require_once __DIR__.'/silex.phar'; 

$app = new Silex\Application(); 

$app->get('/hello/{name}', function($name) use($app) { 
    return 'Hello '.$app->escape($name); 
}); 

$app->run(); 

As I mentioned in the first sentance of the ticket description "This may be merely something to add to the docs."

Because, if you read that, use it and access the url /hello/foo/ it doesn't work. It's not mentioned in the docs and IMO that's not something you want to find out by accident.

Contributor

AD7six commented Aug 16, 2011

Silex-project.org contains this snippet on the first page:

require_once __DIR__.'/silex.phar'; 

$app = new Silex\Application(); 

$app->get('/hello/{name}', function($name) use($app) { 
    return 'Hello '.$app->escape($name); 
}); 

$app->run(); 

As I mentioned in the first sentance of the ticket description "This may be merely something to add to the docs."

Because, if you read that, use it and access the url /hello/foo/ it doesn't work. It's not mentioned in the docs and IMO that's not something you want to find out by accident.

@AD7six

This comment has been minimized.

Show comment
Hide comment
@AD7six

AD7six Aug 16, 2011

Contributor

Perhaps this ticket would be more appropriate lodged against the symfony project itself - unless it already has been and dismissed as wont-fix/works as designed. Based on the information presented it would seem that symfony(2) tries to emulate the way apache[1] works - which .. doesn't seem appropriate since apache's rewrite rules are based around a filesystem and silex/symfony is a framework for handling dynamic requests.

A concequence of the way silex/symfony acts wrt trailing slashes is that to get some kind of consistency you could e.g. put something like this in your index.php file (or modify your rewrite rules) and write routes as silex's docs suggest:

$_SERVER['REQUEST_URI'] = rtrim($_SERVER['REQUEST_URI'], '/');

However that seems to risk certain bad things happening. such as redirect loops and mounted applications possibly being inaccessible. So, instead you might think to use only routes with trailing slashes and do this:

$_SERVER['REQUEST_URI'] = rtrim($_SERVER['REQUEST_URI'], '/') . '/';

Which works. but why is it necesssary.

Things get worse/more complex when considering not-GET requests.

The response of POST, PUT or DELETE requests which don't end in a trailing slash - if the routes have all been defined with a trailing slash - is a 301 redirect to the trailing-slash equivalent url emitted. Thus converting all POST, PUT and DELETE callbacks unreachable.

Otherwise you have to forgo any REQUEST_URI tampering, make all GET handlers have a trailing slash so that they can handle either - and have all other handlers without so that they are reachable (and put up with any 404s that are generated. OR add duplicate handlers).

There are numerous references around e.g. on the silex google group referring to the "trailing slash problem" so is it something that's already known about but thus far not actioned? or is it something I need to patch to be able to continue to use Silex consistently because the existing code isn't going to change behavior even if I send a pull request?

[1] as a side note - why try and emulate the way one webserver works? I don't know of any other mainstream php framework that distinguishes between /foo and /foo/, and apache's behavior in such circumstances is configurable - so why try and emulate one configuration of one webserver?

Contributor

AD7six commented Aug 16, 2011

Perhaps this ticket would be more appropriate lodged against the symfony project itself - unless it already has been and dismissed as wont-fix/works as designed. Based on the information presented it would seem that symfony(2) tries to emulate the way apache[1] works - which .. doesn't seem appropriate since apache's rewrite rules are based around a filesystem and silex/symfony is a framework for handling dynamic requests.

A concequence of the way silex/symfony acts wrt trailing slashes is that to get some kind of consistency you could e.g. put something like this in your index.php file (or modify your rewrite rules) and write routes as silex's docs suggest:

$_SERVER['REQUEST_URI'] = rtrim($_SERVER['REQUEST_URI'], '/');

However that seems to risk certain bad things happening. such as redirect loops and mounted applications possibly being inaccessible. So, instead you might think to use only routes with trailing slashes and do this:

$_SERVER['REQUEST_URI'] = rtrim($_SERVER['REQUEST_URI'], '/') . '/';

Which works. but why is it necesssary.

Things get worse/more complex when considering not-GET requests.

The response of POST, PUT or DELETE requests which don't end in a trailing slash - if the routes have all been defined with a trailing slash - is a 301 redirect to the trailing-slash equivalent url emitted. Thus converting all POST, PUT and DELETE callbacks unreachable.

Otherwise you have to forgo any REQUEST_URI tampering, make all GET handlers have a trailing slash so that they can handle either - and have all other handlers without so that they are reachable (and put up with any 404s that are generated. OR add duplicate handlers).

There are numerous references around e.g. on the silex google group referring to the "trailing slash problem" so is it something that's already known about but thus far not actioned? or is it something I need to patch to be able to continue to use Silex consistently because the existing code isn't going to change behavior even if I send a pull request?

[1] as a side note - why try and emulate the way one webserver works? I don't know of any other mainstream php framework that distinguishes between /foo and /foo/, and apache's behavior in such circumstances is configurable - so why try and emulate one configuration of one webserver?

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Aug 16, 2011

Hello,
consider this examples:

  • /path MAY mean /path/ but it is not equal to
  • /path/ IS NOT /path the same way as
  • file.html/ DOES NOT EQUAL file.html

Some more real life examples:

By requesting /path (when file/route is not found and /path/ exists) Apache and Symfony make it easy for their users and redirect the request to canonical form: /path/. Still it does not mean the URLs are equal. They are different in terms of file structure.

More can be found at Apache docs:

Every webmaster can sing a song about the problem of the trailing slash on URLs 
referencing directories. If they are missing, the server dumps an error, because if you 
say /~quux/foo instead of /~quux/foo/ then the server searches for a file named foo. And 
because this file is a directory it complains. Actually it tries to fix it itself in most 
of the cases, but sometimes this mechanism need to be emulated by you. For instance 
after you have done a lot of complicated URL rewritings to CGI scripts etc.

http://httpd.apache.org/docs/2.0/misc/rewriteguide.html

A "trailing slash" redirect is issued when the server receives a request for a URL 
http://servername/foo/dirname where dirname is a directory. Directories require a 
trailing slash, so mod_dir issues a redirect to http://servername/foo/dirname/.

http://httpd.apache.org/docs/2.0/mod/mod_dir.html

To sump up, if you want to use URLs with trailing slash (directory-like NOT file-like), specify /hello/{name}/ not /hello/{name} in routing. Please note that within Symfony and Silex application generated links are consistent based on specified routing information, so I guess the issue is purely academic.

The observed routing behaviour in the issue description is actually correct behaviuor. So there is nothing to fix here.

Regards,

ghost commented Aug 16, 2011

Hello,
consider this examples:

  • /path MAY mean /path/ but it is not equal to
  • /path/ IS NOT /path the same way as
  • file.html/ DOES NOT EQUAL file.html

Some more real life examples:

By requesting /path (when file/route is not found and /path/ exists) Apache and Symfony make it easy for their users and redirect the request to canonical form: /path/. Still it does not mean the URLs are equal. They are different in terms of file structure.

More can be found at Apache docs:

Every webmaster can sing a song about the problem of the trailing slash on URLs 
referencing directories. If they are missing, the server dumps an error, because if you 
say /~quux/foo instead of /~quux/foo/ then the server searches for a file named foo. And 
because this file is a directory it complains. Actually it tries to fix it itself in most 
of the cases, but sometimes this mechanism need to be emulated by you. For instance 
after you have done a lot of complicated URL rewritings to CGI scripts etc.

http://httpd.apache.org/docs/2.0/misc/rewriteguide.html

A "trailing slash" redirect is issued when the server receives a request for a URL 
http://servername/foo/dirname where dirname is a directory. Directories require a 
trailing slash, so mod_dir issues a redirect to http://servername/foo/dirname/.

http://httpd.apache.org/docs/2.0/mod/mod_dir.html

To sump up, if you want to use URLs with trailing slash (directory-like NOT file-like), specify /hello/{name}/ not /hello/{name} in routing. Please note that within Symfony and Silex application generated links are consistent based on specified routing information, so I guess the issue is purely academic.

The observed routing behaviour in the issue description is actually correct behaviuor. So there is nothing to fix here.

Regards,

@AD7six

This comment has been minimized.

Show comment
Hide comment
@AD7six

AD7six Aug 16, 2011

Contributor

I'm not clear why you keep quoting the apache docs. Are symfony projects designed to only work on/emulate apache?

Contributor

AD7six commented Aug 16, 2011

I'm not clear why you keep quoting the apache docs. Are symfony projects designed to only work on/emulate apache?

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Aug 16, 2011

No, but using some docs and conventions is a good start.

ghost commented Aug 16, 2011

No, but using some docs and conventions is a good start.

@AD7six

This comment has been minimized.

Show comment
Hide comment
@AD7six

AD7six Aug 17, 2011

Contributor

@mheleniak if you could point at anything in the Silex docs, I'd give what you're saying some weight.

Anyway my parting comment on this ticket are that this 'feature' leads to inconsistent behavior. I'm using silex to build a json/xml REST api and the routes a user needs to use depend on whether what they are calling is mounted or not - I don't like that at all.

e.g. using this application: https://gist.github.com/d695da6bc429a1ffcd9e, results are this:
this screenshot

The concequence of Silex's route handling are that when calling the application the url is different if you're posting to a mounted app or not - that means you need intimate knowledge to be able to use it - knowledge you shouldn't have. I don't see how exposing implementation details in such a way that it breaks things (POST, PUT and DELETE requests) is a good idea®. Life would be a lot simpler if Silex/Symfony ignored trailing slashes when matching routes and left to the developer the arduous task of sticking a canonical meta tag in their source or issuing a 301 where relevant.

If nothing else, it should be documented.

Contributor

AD7six commented Aug 17, 2011

@mheleniak if you could point at anything in the Silex docs, I'd give what you're saying some weight.

Anyway my parting comment on this ticket are that this 'feature' leads to inconsistent behavior. I'm using silex to build a json/xml REST api and the routes a user needs to use depend on whether what they are calling is mounted or not - I don't like that at all.

e.g. using this application: https://gist.github.com/d695da6bc429a1ffcd9e, results are this:
this screenshot

The concequence of Silex's route handling are that when calling the application the url is different if you're posting to a mounted app or not - that means you need intimate knowledge to be able to use it - knowledge you shouldn't have. I don't see how exposing implementation details in such a way that it breaks things (POST, PUT and DELETE requests) is a good idea®. Life would be a lot simpler if Silex/Symfony ignored trailing slashes when matching routes and left to the developer the arduous task of sticking a canonical meta tag in their source or issuing a 301 where relevant.

If nothing else, it should be documented.

@evert

This comment has been minimized.

Show comment
Hide comment
@evert

evert Jan 25, 2012

Url ending with / and not ending with slash may canonically not mean the exact same thing, regardless of how you put it.

However, I think that a route with a slash, acting as a fallback to a url without a slash but not the other way round, is indeed plain weird. I would personally opt for a silex-wide setting to disregard any trailing slashes, from the request uri or route.

evert commented Jan 25, 2012

Url ending with / and not ending with slash may canonically not mean the exact same thing, regardless of how you put it.

However, I think that a route with a slash, acting as a fallback to a url without a slash but not the other way round, is indeed plain weird. I would personally opt for a silex-wide setting to disregard any trailing slashes, from the request uri or route.

@piotrgradzinski

This comment has been minimized.

Show comment
Hide comment
@piotrgradzinski

piotrgradzinski Apr 3, 2012

Hi,
here is small improvement for the solution posted above. Everything is ok when you have url like /hello/world and /hello/world/, but problem appears when you have /hello/world/?param=value. Simple solution for this:

$request_uri_rep_count = 1;
$_SERVER['REQUEST_URI'] = str_replace('/?', '?', $_SERVER['REQUEST_URI'], $request_uri_rep_count);
$_SERVER['REQUEST_URI'] = rtrim($_SERVER['REQUEST_URI'], '/') . '/';

Hi,
here is small improvement for the solution posted above. Everything is ok when you have url like /hello/world and /hello/world/, but problem appears when you have /hello/world/?param=value. Simple solution for this:

$request_uri_rep_count = 1;
$_SERVER['REQUEST_URI'] = str_replace('/?', '?', $_SERVER['REQUEST_URI'], $request_uri_rep_count);
$_SERVER['REQUEST_URI'] = rtrim($_SERVER['REQUEST_URI'], '/') . '/';
@LNGi

This comment has been minimized.

Show comment
Hide comment
@LNGi

LNGi May 17, 2012

to me, the simplest solution is add a extra route like this:

photo_show:
  pattern:      /photos/{photo_id}
  defaults:     {_controller: PhotoBundle:Photo:show}
  requirements: {_method: GET}

photo_show_2:
  pattern:      /photos/{photo_id}/
  defaults:     {_controller: PhotoBundle:Photo:show}
  requirements: {_method: GET}

LNGi commented May 17, 2012

to me, the simplest solution is add a extra route like this:

photo_show:
  pattern:      /photos/{photo_id}
  defaults:     {_controller: PhotoBundle:Photo:show}
  requirements: {_method: GET}

photo_show_2:
  pattern:      /photos/{photo_id}/
  defaults:     {_controller: PhotoBundle:Photo:show}
  requirements: {_method: GET}
@evert

This comment has been minimized.

Show comment
Hide comment
@evert

evert May 17, 2012

lianz: it's kind of annoying if you have to specify every route twice :)

evert commented May 17, 2012

lianz: it's kind of annoying if you have to specify every route twice :)

@LNGi

This comment has been minimized.

Show comment
Hide comment
@LNGi

LNGi May 17, 2012

yes,you are right. but for now, we have to use kind of workaround like this.

在 2012-5-17,23:11,evert
reply@reply.github.com
写道:

lianz: it's kind of annoying if you have to specify every route twice :)


Reply to this email directly or view it on GitHub:
https://github.com/fabpot/Silex/issues/149#issuecomment-5765989

LNGi commented May 17, 2012

yes,you are right. but for now, we have to use kind of workaround like this.

在 2012-5-17,23:11,evert
reply@reply.github.com
写道:

lianz: it's kind of annoying if you have to specify every route twice :)


Reply to this email directly or view it on GitHub:
https://github.com/fabpot/Silex/issues/149#issuecomment-5765989

@fabpot

This comment has been minimized.

Show comment
Hide comment
@fabpot

fabpot May 22, 2012

Member

Closing this issue as the current behavior is indeed the one we expect. Some more comments on why we do this:

  • Each resource must have a unique URL (so /foo/ is different from /foo).
  • As a convenience for the end user (not the developer), when a URL must end with a / and if he forgets to type it, Symfony/Silex redirects him to the right URL (with a /) instead of returning a 404.
  • When Symfony/Silex generate URLs, they always use the canonical URL, so the redirection is only done when a user enter a URL manually and forget to add the trailing /, but it is never used by the framework itself.
Member

fabpot commented May 22, 2012

Closing this issue as the current behavior is indeed the one we expect. Some more comments on why we do this:

  • Each resource must have a unique URL (so /foo/ is different from /foo).
  • As a convenience for the end user (not the developer), when a URL must end with a / and if he forgets to type it, Symfony/Silex redirects him to the right URL (with a /) instead of returning a 404.
  • When Symfony/Silex generate URLs, they always use the canonical URL, so the redirection is only done when a user enter a URL manually and forget to add the trailing /, but it is never used by the framework itself.

@fabpot fabpot closed this May 22, 2012

@AD7six

This comment has been minimized.

Show comment
Hide comment
@AD7six

AD7six May 22, 2012

Contributor

-1, obviously - for the reasons summarised by the image in my last reply.

Contributor

AD7six commented May 22, 2012

-1, obviously - for the reasons summarised by the image in my last reply.

@fabpot

This comment has been minimized.

Show comment
Hide comment
@fabpot

fabpot May 22, 2012

Member

@AD7six I've re-read your last comment and I think I understand now what you're talking about. The problem you have occurs for mounted routes right? Not for other types of routes. It that's the case, then I'm going to reopen the ticket and investigate the issue further.

Member

fabpot commented May 22, 2012

@AD7six I've re-read your last comment and I think I understand now what you're talking about. The problem you have occurs for mounted routes right? Not for other types of routes. It that's the case, then I'm going to reopen the ticket and investigate the issue further.

@stof

This comment has been minimized.

Show comment
Hide comment
@stof

stof May 22, 2012

Contributor

@fabpot note that Symfony has exactly the same issue when importing a collection with a prefix

Contributor

stof commented May 22, 2012

@fabpot note that Symfony has exactly the same issue when importing a collection with a prefix

@AD7six

This comment has been minimized.

Show comment
Hide comment
@AD7six

AD7six May 22, 2012

Contributor

@fabpot,

Yes.

  1. not-GET requests to /foo get a 301 to /foo/
  2. Defining routes for mounted apps it's not possible to define a route for /mountedhere. has to be accessed as /mountedhere/. This doens't work either $mounted->post('', ...

The first is anything, you've stated is expected behavior (POST|PUT|DELETE->GET does not help the end user)

The latter is mounted only (but only because you can't have a request for the url "")

Together it's problematic.

The implicit assumption that a silex app will eat it's own dogfood (generate it's own urls) doesn't apply for the use case in this ticket - an api.

I'll point out that I don't use the app any more (or silex) which prompted this ticket - so my knowledge/recollection is possibly now erroneous.

Contributor

AD7six commented May 22, 2012

@fabpot,

Yes.

  1. not-GET requests to /foo get a 301 to /foo/
  2. Defining routes for mounted apps it's not possible to define a route for /mountedhere. has to be accessed as /mountedhere/. This doens't work either $mounted->post('', ...

The first is anything, you've stated is expected behavior (POST|PUT|DELETE->GET does not help the end user)

The latter is mounted only (but only because you can't have a request for the url "")

Together it's problematic.

The implicit assumption that a silex app will eat it's own dogfood (generate it's own urls) doesn't apply for the use case in this ticket - an api.

I'll point out that I don't use the app any more (or silex) which prompted this ticket - so my knowledge/recollection is possibly now erroneous.

@fabpot fabpot reopened this May 22, 2012

@ludofleury

This comment has been minimized.

Show comment
Hide comment
@ludofleury

ludofleury Jun 20, 2012

What's the status of this issue ? we just ran into the same problem. Unsafe HTTP method cannot be redirected with the appropriate method. We tried ->post('' , ...) But it don't behave correctly.

So, after some investigation the problem is exactly on this line https://github.com/symfony/Routing/blob/master/Route.php#L83
Removing this line fixes all my problem for post/put/delete method on an url mounted and not ending with a slash.

I don't know what are the side effect of this, so I wait for the @fabpot response before any PR.

What's the status of this issue ? we just ran into the same problem. Unsafe HTTP method cannot be redirected with the appropriate method. We tried ->post('' , ...) But it don't behave correctly.

So, after some investigation the problem is exactly on this line https://github.com/symfony/Routing/blob/master/Route.php#L83
Removing this line fixes all my problem for post/put/delete method on an url mounted and not ending with a slash.

I don't know what are the side effect of this, so I wait for the @fabpot response before any PR.

@ludofleury

This comment has been minimized.

Show comment
Hide comment
@ludofleury

ludofleury Jun 21, 2012

Many consideration around this issues:

First it's common to add a trailing slash at the end of the url for relative url compilation.
Consider this example:

<a href="beta" />relative url</a>

http://test.tld/alpha/100 -> http://test.tld/alpha/beta
http://test.tld/alpha/100/ -> http://test.tld/alpha/100/beta

Yet, when the url has a query component , I'm more familiar with http://test.tld/alpha?param than http://test.tld/alpha/?param The second url looks weird (?).

Basically, if the Route (https://github.com/symfony/Routing/blob/master/Route.php#L83) doesn't prefix the url pattern with a /

  • pro: explicit behavior matching the discrimination of trailing and non-trailing slash in url
  • cons : code looks weird
<?php
$app->mount('/alpha', $controllersProvider);

$controllers->post('', function() {}); // will match post /alpha
$controllers->get('', function() {}); // will match get /alpha
$controllers->get('/', function() {}); // will match get /alpha/

As far as I know, Silex doesn't allow recursive mount (since the ControllerCollection cannot mount another controller collection) ? So the best of both worlds could be a new rule for the route definition process:
"if a Route contains only a / and is defined by a Silex\ControllerCollection (and is a unsafe method ?): remove the slash"

  • pro: consistency in code (all url start with a /)
  • pro (?): query component are displayed just after the prefix /prefix?param
  • cons: can't handle url with and without the trailing slash
  • cons: could appear as an unexpected behavior from a developer point of view
<?php 
$app->mount('/alpha', $controllersProvider);

$controllers->get('/', function() {}); // will match get /alpha 
// but /alpha/ will fail, 
// it could be reprocessed by the router to match without the trailing slash
// but this is the inversed process as the actual one

Another solution could be a change in the UrlMatcher, if the method is unsafe (post | put | delete) then do not redirect but try to resolve the route on both url.

Notice: I don't find anything sexy in these solutions, sorry :(

Many consideration around this issues:

First it's common to add a trailing slash at the end of the url for relative url compilation.
Consider this example:

<a href="beta" />relative url</a>

http://test.tld/alpha/100 -> http://test.tld/alpha/beta
http://test.tld/alpha/100/ -> http://test.tld/alpha/100/beta

Yet, when the url has a query component , I'm more familiar with http://test.tld/alpha?param than http://test.tld/alpha/?param The second url looks weird (?).

Basically, if the Route (https://github.com/symfony/Routing/blob/master/Route.php#L83) doesn't prefix the url pattern with a /

  • pro: explicit behavior matching the discrimination of trailing and non-trailing slash in url
  • cons : code looks weird
<?php
$app->mount('/alpha', $controllersProvider);

$controllers->post('', function() {}); // will match post /alpha
$controllers->get('', function() {}); // will match get /alpha
$controllers->get('/', function() {}); // will match get /alpha/

As far as I know, Silex doesn't allow recursive mount (since the ControllerCollection cannot mount another controller collection) ? So the best of both worlds could be a new rule for the route definition process:
"if a Route contains only a / and is defined by a Silex\ControllerCollection (and is a unsafe method ?): remove the slash"

  • pro: consistency in code (all url start with a /)
  • pro (?): query component are displayed just after the prefix /prefix?param
  • cons: can't handle url with and without the trailing slash
  • cons: could appear as an unexpected behavior from a developer point of view
<?php 
$app->mount('/alpha', $controllersProvider);

$controllers->get('/', function() {}); // will match get /alpha 
// but /alpha/ will fail, 
// it could be reprocessed by the router to match without the trailing slash
// but this is the inversed process as the actual one

Another solution could be a change in the UrlMatcher, if the method is unsafe (post | put | delete) then do not redirect but try to resolve the route on both url.

Notice: I don't find anything sexy in these solutions, sorry :(

@aloha

This comment has been minimized.

Show comment
Hide comment
@aloha

aloha Jun 26, 2012

I would just like to echo what AD7six said. "This should be documented"

At least mention the Symfony paradigm around route handling on the usage page. If I would have read something about this last night I would have saved myself a bit of time. Now that I know, I like the way it handles things. But for someone just checking out your framework it's easily missed. I'm willing to help if needed.

Cheers

aloha commented Jun 26, 2012

I would just like to echo what AD7six said. "This should be documented"

At least mention the Symfony paradigm around route handling on the usage page. If I would have read something about this last night I would have saved myself a bit of time. Now that I know, I like the way it handles things. But for someone just checking out your framework it's easily missed. I'm willing to help if needed.

Cheers

@fabpot

This comment has been minimized.

Show comment
Hide comment
@fabpot

fabpot Jul 1, 2012

Member

First step: The trailing slash redirection on non-safe method is fixed in Symfony now: symfony/symfony@ed49e3b

Member

fabpot commented Jul 1, 2012

First step: The trailing slash redirection on non-safe method is fixed in Symfony now: symfony/symfony@ed49e3b

@fabpot

This comment has been minimized.

Show comment
Hide comment
@fabpot

fabpot Jul 1, 2012

Member

Let me explain the current behavior of mounted routes so that everybody understand the behavior explained by several people in this thread (and this behavior is the same as the one in Symfony, so it applies to Symfony as well).

The simplest URL possible is / (an empty URL does not mean anything and is silently converted by browsers to /, so when you are requesting http://google.com, the actual request is for http://google.com/).

When you mount a route collection under a prefix, Silex/Symfony creates a new "namespace" for those URLs. When you define / in a route collection and then mount it under /foo, the URL to access this route is /foo/. Requesting /foo in this case does not make sense.

So, as of today, it is not possible to define a callback for /foo when mounting a route collection under /foo. I can see the limitation of the current behavior but I can't think of a possible solution that does not feel hackish.

Member

fabpot commented Jul 1, 2012

Let me explain the current behavior of mounted routes so that everybody understand the behavior explained by several people in this thread (and this behavior is the same as the one in Symfony, so it applies to Symfony as well).

The simplest URL possible is / (an empty URL does not mean anything and is silently converted by browsers to /, so when you are requesting http://google.com, the actual request is for http://google.com/).

When you mount a route collection under a prefix, Silex/Symfony creates a new "namespace" for those URLs. When you define / in a route collection and then mount it under /foo, the URL to access this route is /foo/. Requesting /foo in this case does not make sense.

So, as of today, it is not possible to define a callback for /foo when mounting a route collection under /foo. I can see the limitation of the current behavior but I can't think of a possible solution that does not feel hackish.

@evert

This comment has been minimized.

Show comment
Hide comment
@evert

evert Jul 2, 2012

One solution would be to always strip any trailing slash from both the pattern and the request url. Basically treat it entirely as optional.

evert commented Jul 2, 2012

One solution would be to always strip any trailing slash from both the pattern and the request url. Basically treat it entirely as optional.

@fabpot fabpot referenced this issue in symfony/symfony Jul 10, 2012

Closed

[2.2] Importing Routes (Prefix issue) #4322

@acasademont

This comment has been minimized.

Show comment
Hide comment
@acasademont

acasademont Jul 17, 2012

Hit with this issue in symfony also. @evert proposal could work

Hit with this issue in symfony also. @evert proposal could work

@jrschumacher

This comment has been minimized.

Show comment
Hide comment
@jrschumacher

jrschumacher Nov 14, 2012

Until this issue is decided upon (and for those who are also having trouble) a solution I have taken for some of my clients who can't handle 301 redirects:

// Internally forward requests
$app->match('/{resource}', function($resource) use($app) {
  // Now forward
  $subRequest = Request::create(
      '/'.$resource.'/',
      $app['request']->getMethod(),
      array_merge($app['request']->query->all(), $app['request']->request->all()),
      $app['request']->cookies->all(),
      $app['request']->files->all(),
      $app['request']->server->all(),
      $app['request']->getContent()
    );
  return $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
});

There are some major presumptions with this though and issues with this:

  • must use controllers for each resource (unless you add routes before fix)
  • no dynamic support for prepending routes (i.e. /1/user, /api/1/user) †
  • added overhead

† you can add more variables before to handle this case

$app->match('/{version}/{resource}', function($version, $resource) use($app){});

Until this issue is decided upon (and for those who are also having trouble) a solution I have taken for some of my clients who can't handle 301 redirects:

// Internally forward requests
$app->match('/{resource}', function($resource) use($app) {
  // Now forward
  $subRequest = Request::create(
      '/'.$resource.'/',
      $app['request']->getMethod(),
      array_merge($app['request']->query->all(), $app['request']->request->all()),
      $app['request']->cookies->all(),
      $app['request']->files->all(),
      $app['request']->server->all(),
      $app['request']->getContent()
    );
  return $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
});

There are some major presumptions with this though and issues with this:

  • must use controllers for each resource (unless you add routes before fix)
  • no dynamic support for prepending routes (i.e. /1/user, /api/1/user) †
  • added overhead

† you can add more variables before to handle this case

$app->match('/{version}/{resource}', function($version, $resource) use($app){});
@alexkappa

This comment has been minimized.

Show comment
Hide comment
@alexkappa

alexkappa Nov 17, 2012

Contributor

What's the status on this issue? Is there any chance this is going to be resolved? I second @evert's proposal especially for mounted route collections. I would love to help if I'm needed.

Contributor

alexkappa commented Nov 17, 2012

What's the status on this issue? Is there any chance this is going to be resolved? I second @evert's proposal especially for mounted route collections. I would love to help if I'm needed.

@till

This comment has been minimized.

Show comment
Hide comment
@till

till Apr 23, 2013

If this is how it is — can anyone shed some light on how we can disable the meta-tag refresh?

I would like it to be more explicit and respond with a 404 instead. If anything, I'd go for a server side solution too and use a Location header to redirect the client.

till commented Apr 23, 2013

If this is how it is — can anyone shed some light on how we can disable the meta-tag refresh?

I would like it to be more explicit and respond with a 404 instead. If anything, I'd go for a server side solution too and use a Location header to redirect the client.

@igorw

This comment has been minimized.

Show comment
Hide comment
@igorw

igorw Apr 23, 2013

Contributor

@till to completely disable the redirection behaviour, replace the RedirectableUrlMatcher with a UrlMatcher:

use Symfony\Component\Routing\Matcher\UrlMatcher;

$app['url_matcher'] = $app->share(function () use ($app) {
    return new UrlMatcher($app['routes'], $app['request_context']);
});
Contributor

igorw commented Apr 23, 2013

@till to completely disable the redirection behaviour, replace the RedirectableUrlMatcher with a UrlMatcher:

use Symfony\Component\Routing\Matcher\UrlMatcher;

$app['url_matcher'] = $app->share(function () use ($app) {
    return new UrlMatcher($app['routes'], $app['request_context']);
});
@igorw

This comment has been minimized.

Show comment
Hide comment
@igorw

igorw Apr 23, 2013

Contributor

Note: the default behaviour of silex does include a location header and a 302 status. The meta redirect is just an additional fallback for netscape or IE5 or something.

Contributor

igorw commented Apr 23, 2013

Note: the default behaviour of silex does include a location header and a 302 status. The meta redirect is just an additional fallback for netscape or IE5 or something.

@till

This comment has been minimized.

Show comment
Hide comment
@till

till Apr 23, 2013

@igorw Is this a bug then? I keep getting this always. I have no idea how or where we turned this on.

till commented Apr 23, 2013

@igorw Is this a bug then? I keep getting this always. I have no idea how or where we turned this on.

@stof

This comment has been minimized.

Show comment
Hide comment
@stof

stof Apr 23, 2013

Contributor

@till Maybe you have a file with leading spaces triggering the headers before silex sends the Response (thus not setting the status code header) ?

Contributor

stof commented Apr 23, 2013

@till Maybe you have a file with leading spaces triggering the headers before silex sends the Response (thus not setting the status code header) ?

@till

This comment has been minimized.

Show comment
Hide comment
@till

till Apr 23, 2013

@stof Not aware of any. Do you have an idea what I could check?

till commented Apr 23, 2013

@stof Not aware of any. Do you have an idea what I could check?

@igorw

This comment has been minimized.

Show comment
Hide comment
@igorw

igorw Apr 23, 2013

Contributor

@till just to be clear. You should be getting both. a 302 location redirect in the header and a meta redirect in the body. the RedirectResponse always delivers both. (and you can tweak the RedirectResponse with an after middleware if you want)

Contributor

igorw commented Apr 23, 2013

@till just to be clear. You should be getting both. a 302 location redirect in the header and a meta redirect in the body. the RedirectResponse always delivers both. (and you can tweak the RedirectResponse with an after middleware if you want)

@till

This comment has been minimized.

Show comment
Hide comment
@till

till Apr 23, 2013

@igorw Ahhh! ;) We'll investigate this tomorrow.

till commented Apr 23, 2013

@igorw Ahhh! ;) We'll investigate this tomorrow.

@robertocr

This comment has been minimized.

Show comment
Hide comment
@robertocr

robertocr Oct 9, 2013

I think there should be a place to set up the default behavior for the trailing slashes redirection!
In other words, I should be able to define this:
$app->get('/courses', function () use ($app) {
//controller and models define $params
return $app['twig']->render('courses.html', $params);
});
without having to worry about having to do this:
$app->get('/courses/', function () use ($app) {
return $app->redirect('/courses');
});
or am I missing something / is there a way to do this already?
Thanks

I think there should be a place to set up the default behavior for the trailing slashes redirection!
In other words, I should be able to define this:
$app->get('/courses', function () use ($app) {
//controller and models define $params
return $app['twig']->render('courses.html', $params);
});
without having to worry about having to do this:
$app->get('/courses/', function () use ($app) {
return $app->redirect('/courses');
});
or am I missing something / is there a way to do this already?
Thanks

@devsdmf

This comment has been minimized.

Show comment
Hide comment
@devsdmf

devsdmf Nov 26, 2014

No fix yet?

devsdmf commented Nov 26, 2014

No fix yet?

@GDmac

This comment has been minimized.

Show comment
Hide comment
@GDmac

GDmac Oct 8, 2015

This still gives a strange situation where:

- CalenderController implements ControllerProviderInterface ...
- Route: $ctl->get('/feed/{duration}', array($this, 'feed'))->value('duration',8);

http://local.dev/calendar/feed (default value for parameter)
http://local.dev/calendar/feed/12 (value 12 for parameter)
http://local.dev/calendar/feed/ (ERROR)

The parameters are not inserted into the url when the defined bound route has no trailing slash defined

Edit: The default value for a parameter is only inserted when there is a trailing slash, my bad

  // Route: $ctl->get('/feed/{duration}', array($this, 'feed'))
  $params = array('duration'=>8);
  $feedlink = $this->app['url_generator']->generate('calendarfeed', $params, true);
  // Gives: http://local.dev/calendar/feed

  // Route: (with trailing slash) $ctl->get('/feed/{duration}/', array($this, 'feed'))
  $params = array('duration'=>8);
  $feedlink = $this->app['url_generator']->generate('calendarfeed', $params, true);
  // Gives: http://local.dev/calendar/feed/8/

GDmac commented Oct 8, 2015

This still gives a strange situation where:

- CalenderController implements ControllerProviderInterface ...
- Route: $ctl->get('/feed/{duration}', array($this, 'feed'))->value('duration',8);

http://local.dev/calendar/feed (default value for parameter)
http://local.dev/calendar/feed/12 (value 12 for parameter)
http://local.dev/calendar/feed/ (ERROR)

The parameters are not inserted into the url when the defined bound route has no trailing slash defined

Edit: The default value for a parameter is only inserted when there is a trailing slash, my bad

  // Route: $ctl->get('/feed/{duration}', array($this, 'feed'))
  $params = array('duration'=>8);
  $feedlink = $this->app['url_generator']->generate('calendarfeed', $params, true);
  // Gives: http://local.dev/calendar/feed

  // Route: (with trailing slash) $ctl->get('/feed/{duration}/', array($this, 'feed'))
  $params = array('duration'=>8);
  $feedlink = $this->app['url_generator']->generate('calendarfeed', $params, true);
  // Gives: http://local.dev/calendar/feed/8/
@sjparkinson

This comment has been minimized.

Show comment
Hide comment
@sjparkinson

sjparkinson Nov 27, 2015

Here's an iteration on @jrschumacher's meta-route above, which doesn't handle GET requests.

I've also made this into a controller provider, https://github.com/graze/silex-trailing-slash-handler.

// Silex does not handle POST, PUT, and DELETE requests that are missing a trailing
// slash in the url. This meta-route appends the missing slash to such requests.
//
// We override the default RedirectableUrlMatcher so that it doesn't respond with 301 to GET requests
// missing a trailing slash, so that like POST requests (and the rest), the internal.trailing_slash_redirect
// route will handle it.
$app['url_matcher'] = $app->share(function () use ($app) {
    return new UrlMatcher($app['routes'], $app['request_context']);
});

$app->match('/{resource}', function($resource) use ($app) {
    if ($app['logger']) {
        $app['logger']->debug(sprintf('Appending a trailing slash for the request to `/%s`.', $resource));
    }

    $request = Request::create(
        '/' . $resource . '/',
        $app['request']->getMethod(),
        array_merge($app['request']->query->all(), $app['request']->request->all()),
        $app['request']->cookies->all(),
        $app['request']->files->all(),
        $app['request']->server->all(),
        $app['request']->getContent()
    );

    // Make an internal request based off the one that would have 404'd.
    // http://silex.sensiolabs.org/doc/usage.html#forwards
    return $app->handle($request, HttpKernelInterface::SUB_REQUEST);
})->assert('resource', '.*(?<!\/)$')->bind('internal.trailing_slash_redirect');

Here's an iteration on @jrschumacher's meta-route above, which doesn't handle GET requests.

I've also made this into a controller provider, https://github.com/graze/silex-trailing-slash-handler.

// Silex does not handle POST, PUT, and DELETE requests that are missing a trailing
// slash in the url. This meta-route appends the missing slash to such requests.
//
// We override the default RedirectableUrlMatcher so that it doesn't respond with 301 to GET requests
// missing a trailing slash, so that like POST requests (and the rest), the internal.trailing_slash_redirect
// route will handle it.
$app['url_matcher'] = $app->share(function () use ($app) {
    return new UrlMatcher($app['routes'], $app['request_context']);
});

$app->match('/{resource}', function($resource) use ($app) {
    if ($app['logger']) {
        $app['logger']->debug(sprintf('Appending a trailing slash for the request to `/%s`.', $resource));
    }

    $request = Request::create(
        '/' . $resource . '/',
        $app['request']->getMethod(),
        array_merge($app['request']->query->all(), $app['request']->request->all()),
        $app['request']->cookies->all(),
        $app['request']->files->all(),
        $app['request']->server->all(),
        $app['request']->getContent()
    );

    // Make an internal request based off the one that would have 404'd.
    // http://silex.sensiolabs.org/doc/usage.html#forwards
    return $app->handle($request, HttpKernelInterface::SUB_REQUEST);
})->assert('resource', '.*(?<!\/)$')->bind('internal.trailing_slash_redirect');
@ragboyjr

This comment has been minimized.

Show comment
Hide comment
@ragboyjr

ragboyjr May 3, 2016

Contributor

So after looking through the code on this issue. The crux of the problem lies here: https://github.com/symfony/routing/blob/master/Route.php#L158

The path is always prefixed with a /. Normally this isn't an issue except for the case of mounted routes (prefixed routes) as discussed in this issue.

I did some debugging, and if you remove that line of code about the prepending slash, it all works. Because the Symfony route variables are marked private, you can't update them by extending the Route class. I think this is more of a Symfony issue than Silex.

A nice solution would be to have a route implementation that doesn't automatically prefix the path, and then when you define a route with a prefix, then defining the route as $app->get('', function(){}) will actually work now. And for all other routes defined, you should always include the /. But this will be left up to the user to do that.

$forum = $app['controllers_factory'];
$forum->get('', function () {
    return 'Forum home page';
});

// define "global" controllers
$app->get('/', function () use ($app) {
    dump($app['routes']->all());exit;
    return 'Main home page';
});
$app->get('/abc', function() {
    return 'abc';
});
$app->mount('/forum', $forum);
then the valid routes are:
/
/abc
/forum
Contributor

ragboyjr commented May 3, 2016

So after looking through the code on this issue. The crux of the problem lies here: https://github.com/symfony/routing/blob/master/Route.php#L158

The path is always prefixed with a /. Normally this isn't an issue except for the case of mounted routes (prefixed routes) as discussed in this issue.

I did some debugging, and if you remove that line of code about the prepending slash, it all works. Because the Symfony route variables are marked private, you can't update them by extending the Route class. I think this is more of a Symfony issue than Silex.

A nice solution would be to have a route implementation that doesn't automatically prefix the path, and then when you define a route with a prefix, then defining the route as $app->get('', function(){}) will actually work now. And for all other routes defined, you should always include the /. But this will be left up to the user to do that.

$forum = $app['controllers_factory'];
$forum->get('', function () {
    return 'Forum home page';
});

// define "global" controllers
$app->get('/', function () use ($app) {
    dump($app['routes']->all());exit;
    return 'Main home page';
});
$app->get('/abc', function() {
    return 'abc';
});
$app->mount('/forum', $forum);
then the valid routes are:
/
/abc
/forum
@alexkappa

This comment has been minimized.

Show comment
Hide comment
@alexkappa

alexkappa May 3, 2016

Contributor

@ragboyjr you might be onto something here! Perhaps there's a way to have minimal impact in Symfony's routing and resolve this issue within Silex itself.

We could change the accessors in Symfony\Component\Routing\Route to protected and from Silex\Route overwrite the setRoute method to not prefix routes with a slash.

Thoughts?

Contributor

alexkappa commented May 3, 2016

@ragboyjr you might be onto something here! Perhaps there's a way to have minimal impact in Symfony's routing and resolve this issue within Silex itself.

We could change the accessors in Symfony\Component\Routing\Route to protected and from Silex\Route overwrite the setRoute method to not prefix routes with a slash.

Thoughts?

@ragboyjr

This comment has been minimized.

Show comment
Hide comment
@ragboyjr

ragboyjr May 3, 2016

Contributor

@alexkappa

Yes,

  1. You'd need to make the variables protected in Symfony first.
  2. Then I was thinking, in the Silex\Route, I was thinking we could add a flag for (is prefixed). If that flag is not set, then we just use the parent::setPath, if it is set, then we just set the path without the prefix.

So this change would only affect mounted routes in silex. This wouldn't be a BC break for Symfony. And then we'd need to update the documentation to reflect this specific change.

Contributor

ragboyjr commented May 3, 2016

@alexkappa

Yes,

  1. You'd need to make the variables protected in Symfony first.
  2. Then I was thinking, in the Silex\Route, I was thinking we could add a flag for (is prefixed). If that flag is not set, then we just use the parent::setPath, if it is set, then we just set the path without the prefix.

So this change would only affect mounted routes in silex. This wouldn't be a BC break for Symfony. And then we'd need to update the documentation to reflect this specific change.

@ragboyjr

This comment has been minimized.

Show comment
Hide comment
@ragboyjr

ragboyjr May 3, 2016

Contributor

@alexkappa, the boys at Symfony don't like the idea of making Route class with protected vars. Updating the setPath to take flag might work, but I'm not sure on how we'd get that to filter back down into silex because it's BC incompatible change with regards to extending the route class.

Contributor

ragboyjr commented May 3, 2016

@alexkappa, the boys at Symfony don't like the idea of making Route class with protected vars. Updating the setPath to take flag might work, but I'm not sure on how we'd get that to filter back down into silex because it's BC incompatible change with regards to extending the route class.

@alexkappa

This comment has been minimized.

Show comment
Hide comment
@alexkappa

alexkappa May 10, 2016

Contributor

I was thinking we could add a flag for (is prefixed). If that flag is not set, then we just use the parent::setPath, if it is set, then we just set the path without the prefix.

Yes something along those lines, but how will users pass that $isPrefixed argument? The Route::setPath() method is called from Silex\ControllerCollection.

I was thinking that perhaps we could supply a separate Route class which users can use instead of the original Silex\Route. Silex\Application is configurable, and the route_class and route_factory can be swapped for something else, so users can configure their $app with whatever routing they prefer. The downside however is that we'll introduce two very similar routing classes and people won't obviously see the difference.

Maybe configuring Silex\Route with options which it already supports and replace route_factory to set this option.

Contributor

alexkappa commented May 10, 2016

I was thinking we could add a flag for (is prefixed). If that flag is not set, then we just use the parent::setPath, if it is set, then we just set the path without the prefix.

Yes something along those lines, but how will users pass that $isPrefixed argument? The Route::setPath() method is called from Silex\ControllerCollection.

I was thinking that perhaps we could supply a separate Route class which users can use instead of the original Silex\Route. Silex\Application is configurable, and the route_class and route_factory can be swapped for something else, so users can configure their $app with whatever routing they prefer. The downside however is that we'll introduce two very similar routing classes and people won't obviously see the difference.

Maybe configuring Silex\Route with options which it already supports and replace route_factory to set this option.

@ragboyjr

This comment has been minimized.

Show comment
Hide comment
@ragboyjr

ragboyjr May 10, 2016

Contributor

@alexkappa, not a bad idea at all! Here's a mock I did, and it actually works!

ragboyjr@9eb3a45

let me know what you think

Contributor

ragboyjr commented May 10, 2016

@alexkappa, not a bad idea at all! Here's a mock I did, and it actually works!

ragboyjr@9eb3a45

let me know what you think

@ragboyjr

This comment has been minimized.

Show comment
Hide comment
@ragboyjr

ragboyjr May 10, 2016

Contributor

Also, @alexkappa, implementing this could be done kind of two ways. We either make the user explicitly choose to allow empty routes, or we implicitly allow it by only making mounted routes empty.

For explicitly, we could easily provide a parameter in the RoutingServiceProvider like

$app['allow_empty_routes'] = true;

and that will use the appropriate Route class in the application.

For implicitly, we could setup any mounted controller collection to use EmptyPathRoute as the default route instead of the normal route. So on non-mounted paths, all routes defined are on the normal route. And on mounted routes, all routes defined use the EmptyPathRoutes.

Both options wouldn't be very difficult to do.

Contributor

ragboyjr commented May 10, 2016

Also, @alexkappa, implementing this could be done kind of two ways. We either make the user explicitly choose to allow empty routes, or we implicitly allow it by only making mounted routes empty.

For explicitly, we could easily provide a parameter in the RoutingServiceProvider like

$app['allow_empty_routes'] = true;

and that will use the appropriate Route class in the application.

For implicitly, we could setup any mounted controller collection to use EmptyPathRoute as the default route instead of the normal route. So on non-mounted paths, all routes defined are on the normal route. And on mounted routes, all routes defined use the EmptyPathRoutes.

Both options wouldn't be very difficult to do.

@GDmac GDmac referenced this issue in sabre-io/Baikal Jun 26, 2016

Closed

Silex rewrite #558

@sbczk

This comment has been minimized.

Show comment
Hide comment
@sbczk

sbczk Mar 19, 2017

Contributor

I personally found this way more elegant instead of making sub requests

$app = new Application();

$app->mount('/v1', new SomethingListProvider());
$app->mount('/v1/something', new SomethingRestProvider());

class SomethingListProvider implements ControllerProviderInterface
{
    public function connect(Application $app)
    {
        $app['something_controller'] = function () use ($app) {
            return new SomethingController($app);
        };

        $controller = $app['controllers_factory'];
        $controller->get('/something', 'something_controller:listAction')->bind(
            'something_list'
        );

        return $controller;
    }
}

class SomethingRestProvider implements ControllerProviderInterface
{
    public function connect(Application $app)
    {
        $app['something_controller'] = function() use ($app) {
            return new SomethingController($app);
        };

        $controller = $app['controllers_factory'];
        $controller->get('/{id}', 'something_controller:getAction')->bind(
            'something_get'
        );

        return $controller;
    }
}
Contributor

sbczk commented Mar 19, 2017

I personally found this way more elegant instead of making sub requests

$app = new Application();

$app->mount('/v1', new SomethingListProvider());
$app->mount('/v1/something', new SomethingRestProvider());

class SomethingListProvider implements ControllerProviderInterface
{
    public function connect(Application $app)
    {
        $app['something_controller'] = function () use ($app) {
            return new SomethingController($app);
        };

        $controller = $app['controllers_factory'];
        $controller->get('/something', 'something_controller:listAction')->bind(
            'something_list'
        );

        return $controller;
    }
}

class SomethingRestProvider implements ControllerProviderInterface
{
    public function connect(Application $app)
    {
        $app['something_controller'] = function() use ($app) {
            return new SomethingController($app);
        };

        $controller = $app['controllers_factory'];
        $controller->get('/{id}', 'something_controller:getAction')->bind(
            'something_get'
        );

        return $controller;
    }
}
@Sentence

This comment has been minimized.

Show comment
Hide comment
@Sentence

Sentence Oct 31, 2017

Check this Symfony mini-doc, I hope it helps:
Redirect URLs with a Trailing Slash

Also in Symfony if you use your routes with trailing slashes, the non-trailing slashed routes will 301 redirected to tralining slashed routes.

Check this Symfony mini-doc, I hope it helps:
Redirect URLs with a Trailing Slash

Also in Symfony if you use your routes with trailing slashes, the non-trailing slashed routes will 301 redirected to tralining slashed routes.

@krazijames krazijames referenced this issue in ridi/intranet Jan 29, 2018

Merged

Fix trailing slash bug #122

@fabpot

This comment has been minimized.

Show comment
Hide comment

@fabpot fabpot closed this Mar 22, 2018

@evert

This comment has been minimized.

Show comment
Hide comment
@evert

evert Mar 22, 2018

It's nice to get some closure on this one =D

evert commented Mar 22, 2018

It's nice to get some closure on this one =D

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.