> Symfony中文手册 > 如何定义路由条件

如何定义路由条件

路由的requirements(条件),可以令特定的路由只匹配特定的条件(conditions)。最简单的例子,就是限制一个路由的{通配符}只匹配某些表达式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// src/AppBundle/Controller/BlogController.php
namespace AppBundle\Controller;
 
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
 
class BlogController extends Controller
{
    /**
     * @Route("/blog/{page}", name="blog_list", requirements={"page": "\d+"})
     */
    public function listAction($page)
    {
        // ...
    }
}
1
2
3
4
5
6
# app/config/routing.yml
blog_list:
    path:      /blog/{page}
    defaults:  { _controller: AppBundle:Blog:list }
    requirements:
        page: '\d+'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="Http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        http://symfony.com/schema/routing/routing-1.0.xsd">
 
    <route id="blog_list" path="/blog/{page}">
        <default key="_controller">AppBundle:Blog:list</default>
        <requirement key="page">\d+</requirement>
    </route>
 
    <!-- ... -->
</routes>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
 
$collection = new RouteCollection();
$collection->add('blog_list', new Route('/blog/{page}', array(
    '_controller' => 'AppBundle:Blog:list',
), array(
    'page' => '\d+'
)));
 
// ...
 
return $collection;

得益于\d+条件(它可以是一个任意长度的数字),/blog/2将匹配这个路由,但/blog/my-blog-post则不匹配。

早出现的路由总是胜出

为什么你要特别小心路由的条件?如果一个请求匹配了两个路由,那么第一个路由始终胜出。通过添加条件给第一个路由,你可以令每个路由都在正确的场合下进行匹配。参考添加{通配符}条件作为示例。

由于参数要求是正则表达式,每个条件的复杂程度和灵活性都完全由你控制。假定你的程序首页基于不同的URL可以使用两种不同的语言:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/AppBundle/Controller/MainController.php
 
// ...
class MainController extends Controller
{
    /**
     * @Route("/{_locale}", defaults={"_locale": "en"}, requirements={
     *     "_locale": "en|fr"
     * })
     */
    public function homepageAction($_locale)
    {
    }
}
1
2
3
4
5
6
# app/config/routing.yml
homepage:
    path:      /{_locale}
    defaults:  { _controller: AppBundle:Main:homepage, _locale: en }
    requirements:
        _locale:  en|fr
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        http://symfony.com/schema/routing/routing-1.0.xsd">
 
    <route id="homepage" path="/{_locale}">
        <default key="_controller">AppBundle:Main:homepage</default>
        <default key="_locale">en</default>
        <requirement key="_locale">en|fr</requirement>
    </route>
</routes>
1
2
3
4
5
6
7
8
9
10
11
12
13
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
 
$collection = new RouteCollection();
$collection->add('homepage', new Route('/{_locale}', array(
    '_controller' => 'AppBundle:Main:homepage',
    '_locale'     => 'en',
), array(
    '_locale' => 'en|fr',
)));
 
return $collection;

根据传入的请求,URL的{_locale}部分要匹配正则表达式(en|fr)

Path/路径 Parameters/参数
/ {_locale} = "en"
/en {_locale} = "en"
/fr {_locale} = "fr"
/es won't match this route

路由条件中也可以包含容器参数,这篇文章对此进行了解释。当你的程序正则非常复杂且多次使用时,这会非常好用。

添加HTTP方法的条件 ¶

除了URL,你还能对传入的请求的“方法(method)”进行匹配(如GET、HEAD、POST、PUT、DELETE)。假设你为你的博客创建了一个API,并且你有两个路由:一个用于显示主题(GET请求或者HEAD请求),一个用于更新主题(PUT请求),这可以通过以下路由配置来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// src/AppBundle/Controller/MainController.php
namespace AppBundle\Controller;
 
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
// ...
 
class BlogApiController extends Controller
{
    /**
     * @Route("/api/posts/{id}")
     * @Method({"GET","HEAD"})
     */
    public function showAction($id)
    {
        // ... return a json response with the post
    }
 
    /**
     * @Route("/api/posts/{id}")
     * @Method("PUT")
     */
    public function editAction($id)
    {
        // ... edit a post
    }
}
1
2
3
4
5
6
7
8
9
10
# app/config/routing.yml
api_post_show:
    path:     /api/posts/{id}
    defaults: { _controller: AppBundle:BlogApi:show }
    methods:  [GET, HEAD]

api_post_edit:
    path:     /api/posts/{id}
    defaults: { _controller: AppBundle:BlogApi:edit }
    methods:  [PUT]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        http://symfony.com/schema/routing/routing-1.0.xsd">
 
    <route id="api_post_show" path="/api/posts/{id}" methods="GET|HEAD">
        <default key="_controller">AppBundle:BlogApi:show</default>
    </route>
 
    <route id="api_post_edit" path="/api/posts/{id}" methods="PUT">
        <default key="_controller">AppBundle:BlogApi:edit</default>
    </route>
</routes>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
 
$collection = new RouteCollection();
$collection->add('api_post_show', new Route('/api/posts/{id}', array(
    '_controller' => 'AppBundle:BlogApi:show',
), array(), array(), '', array(), array('GET', 'HEAD')));
 
$collection->add('api_post_edit', new Route('/api/posts/{id}', array(
    '_controller' => 'AppBundle:BlogApi:edit',
), array(), array(), '', array(), array('PUT')));
 
return $collection;

尽管事实上这两个路由有着相同的路径(/api/posts/{id}),但第1个路由将只匹配GET请求或者HEAD请求,而第2个路由将只匹配PUT请求。这就意味着你可以通过同样的URL去显示和提交表单,却能调用控制器的两个不同的action。

如果没有methods被指定,这个路由将匹配所有方法(methods)。

如果你使用HTML表而且HTTP methods不同于GETPOST,你需要包容一个_method参数(parameter)来令HTTP方法无效化(fake)。参考如何改变表单的Action和Method以了解更多。

添加主机条件 ¶

你也可以匹配HTTP上的主机传入的请求。欲了解更多请求,请参阅 如何基于Host来匹配路由 。

利用表达式添加动态条件 ¶

对于极为复杂的条件,你应该使用动态的表达式以匹配请求中的任何 信息。参考如何通过条件来限制路由匹配。