Commit b4c3ffba authored by Fabien Potencier's avatar Fabien Potencier

implemented the same behavior as Symfony2 for URLs ending with /

If a route ends with a / and the user forgets the / (and a similar route without a / does not exist),
then, the user will be redirected to the real URL:

If you have a /doc/ URL pattern, a request to /doc will redirect you to /doc/

This is especially useful for the root URL (/):

/index.php (which does not make sense as the path info is / at a minimum)
redirects to /index.php/
parent 6dfc390b
......@@ -27,10 +27,10 @@ use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\Matcher\Exception\MethodNotAllowedException;
use Symfony\Component\Routing\Matcher\Exception\NotFoundException;
use Symfony\Component\ClassLoader\UniversalClassLoader;
use Silex\RedirectableUrlMatcher;
/**
* The Silex framework class.
......@@ -278,7 +278,7 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
$this['controllers']->flush();
$matcher = new UrlMatcher($this['routes'], array(
$matcher = new RedirectableUrlMatcher($this['routes'], array(
'base_url' => $this['request']->getBaseUrl(),
'method' => $this['request']->getMethod(),
'host' => $this['request']->getHost(),
......
<?php
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Silex;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface;
use Symfony\Component\Routing\Matcher\Exception\NotFoundException;
/**
* @author Fabien Potencier <fabien@symfony.com>
*/
class RedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface
{
private $trailingSlashTest = false;
/**
* @see UrlMatcher::match()
*/
public function match($pathinfo)
{
try {
$parameters = parent::match($pathinfo);
} catch (NotFoundException $e) {
if ('/' === substr($pathinfo, -1)) {
throw $e;
}
// try with a / at the end
$this->trailingSlashTest = true;
return $this->match($pathinfo.'/');
}
if ($this->trailingSlashTest) {
$this->trailingSlashTest = false;
return $this->redirect($pathinfo, null);
}
return $parameters;
}
/**
* @see RedirectableUrlMatcherInterface::match()
*/
public function redirect($pathinfo, $route)
{
return array(
'_controller' => function ($url) { return new RedirectResponse($url, 301); },
'url' => $this->context['base_url'].$pathinfo,
);
}
}
......@@ -159,6 +159,20 @@ class RouterTest extends \PHPUnit_Framework_TestCase
}
}
public function testTrailingSlashBehavior()
{
$app = new Application();
$app->get('/foo/', function() use ($app) {
return new Response('ok');
});
$request = Request::create('/foo');
$response = $app->handle($request);
$this->assertEquals(301, $response->getStatusCode());
$this->assertEquals('/foo/', $response->headers->get('Location'));
}
protected function checkRouteResponse($app, $path, $expectedContent, $method = 'get', $message = null)
{
$request = Request::create($path, $method);
......
Subproject commit a988a84be5405cae3c7084dfaa1de23b782b5787
Subproject commit 59cf0bfbae1907c997437d140355a192bf1ec669
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment