Как определять требования маршрутов

Как определять требования маршрутов

Требования маршрутов могут быть использованы так, чтобы определённый маршрут соответствовал только при определённых условиях. Простейший пример включает в себя ограничение маршрута {wildcard} так, чтобы он соответствовал только некоторым регулярным выражениям:

  • Annotations
     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)
        {
            // ...
        }
    }
    
  • YAML
    1
    2
    3
    4
    5
    6
    # app/config/routing.yml
    blog_list:
        path:      /blog/{page}
        defaults:  { _controller: AppBundle:Blog:list }
        requirements:
            page: '\d+'
    
  • XML
     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>
    
  • PHP
     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/some-string - не будет.

Зачем вам вообще думать о требованиях? Если запрос соответствует двум маршрутам, то первый маршрут всегда будет выигрывать. Путём добавления требований к первому маршруту, вы можете сделать так, чтобы каждый маршрут соответствовал только в правильных ситуациях. См. Требования маршрутизации, чтобы увидеть примеры.

Так как требования параметров - это регулярный выражения, сложность и гибкость каждого требования полностью зависит от вас. Представьте, что домашняя страница вашего приложения доступна на двух разных языках, основываясь на URL:

  • Annotations
     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)
        {
        }
    }
    
  • YAML
    1
    2
    3
    4
    5
    6
    # app/config/routing.yml
    homepage:
        path:      /{_locale}
        defaults:  { _controller: AppBundle:Main:homepage, _locale: en }
        requirements:
            _locale:  en|fr
    
  • XML
     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>
    
  • PHP
     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).

Путь Параметры
/ {_locale} = "en"
/en {_locale} = "en"
/fr {_locale} = "fr"
/es не будет соответствовать этому маршруту

Note

Начиная с Symfony 3.2, вы можете активировать соответствие маршрутов UTF-8, установив опцию utf8 при объявлении или импорте маршрутов. Таким образом, к примеру, . в требованиях будет соответствовать любым символам UTF-8, а не одному байту. Опция автоматически подключается каждый раз, когда маршрут или требование использует любые символы UTF-8 не являющиеся ASCII или свойство PCRE Unicode (\p{xx}, \P{xx} или \X). Отметьте, что это поведение устарело, и в версии 4.0 вместо этого будет использовано LogicException, если вы ясно не укажете опцию utf8.

Tip

Требования маршрутов также могут включать в себя параметры контейнеров, как объявсняется в этой статье. Это может быть полезным, когда регулярное выражение очень сложное и многократно используется в вашем приложении.

Добавление требований HTTP-методов

В дополнение к URL, вы также можете использовать соответствие в методе входящего запроса (т.е. GET, HEAD, POST, PUT, DELETE). Представьте, что вы создаёте API для вашего блога, и у вас есть 2 маршрута: один для отображения записи (по запросу GET или HEAD), и один для обновления записи (по запросу PUT). Этого можно достичь с помощью следующей конфигурации маршрута:

  • Annotations
     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/BlogApiController.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)
        {
            // ... вернуть JSON-ответ с записью
        }
    
        /**
         * @Route("/api/posts/{id}")
         * @Method("PUT")
         */
        public function editAction($id)
        {
            // ... изменить запись
        }
    }
    
  • YAML
     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]
    
  • XML
     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>
    
  • PHP
     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}), первый маршрут будет соответствовать только запросам GET или HEAD, а второй - только запросам PUT. Это означает, что вы можете отобразить и изменить запись с одним и тем же URL, используя отдельные контроллеры для двух действий.

Note

Если не указано никаких methods (методов), то маршрут будет соответствовать всем методам.

Tip

Если вы используете HTML-формы и HTTP-методы кроме GET и POST, вам понадобится включить параметр _method чтобы сымитировать HTTP-метод. См. How to Change the Action and Method of a Form, чтобы узнать больше.

Добавление требования хоста

Вы также можете использовать соответствие в HTTP хосте входящего запроса. Чтобы узнать больше об этом, см. How to Match a Route Based on the Host в документации компонента Маршрутизация.

Добавление динамических требований с выражениями

Для очень сложных требований вы можете использовать динамические выражения для соответствия любой информации по запросу. Смотрите Условия маршрутизации.

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.