Маршрутизация
Дата обновления перевода 2024-08-01
Маршрутизация
Когда ваше приложение получает запрос, оно вызывает действие контроллера,
чтобы сгенерировать ответ. Конфигурациия маршутизации определяет, какое действие выполнять для
каждого входящего URL. Она также предоставляет другие полезные функции, вроде генерирования дружелюбных
для SEO URL (например, /read/intro-to-symfony
вместо index.php?article_id=57
).
Создание маршрутов
Маршруты могут быть сконфигурированы на YAML, XML, PHP или с использованием атрибутов или аннотаций. Все форматы предоставляют одинаковые функции и производительность, поэтому выбирайте то, что вам больше нравится. Symfony рекомендует атрибуты , так как это удобно - помещать маршрут и контроллер в одно место.
Создание маршрутов как атрибутов
PHP-атрибуты позволяют определять маршруты рядом с кодом контроллеров, ассоциированных с этими маршрутами. Атрибуты нативны в PHP 8 и новее, поэтому вы можете использовать их сразу же.
Вам нужно добавить немного конфигурации к вашему проекту перед их использованием. Если ваш проект использует Symfony Flex , этот файл уже создан за вас. Иначе, создайте следующий файл вручную:
1 2 3 4 5 6 7 8 9 10
# config/routes/attributes.yaml
controllers:
resource:
path: ../../src/Controller/
namespace: App\Controller
type: attribute
kernel:
resource: App\Kernel
type: attribute
Эта конфигурация сообщает Symfony искать маршруты, определенные как атрибуты, в классах,
объявленных в пространстве имен App\Controller
, и хранящихся в каталоге src/Controller/
,
которая следует стандарту PSR-4. Ядро тоже может действовать как контроллер, что особенно
полезно для маленьких приложений, которые используют Symfony в качестве микрофеймворка.
Представьте, что вы хотите определить маршрут для URL /blog
в вашем приложении.
Чтобы сделать это, создайте класс контроллера как показано ниже:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/Controller/BlogController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class BlogController extends AbstractController
{
#[Route('/blog', name: 'blog_list')]
public function list(): Response
{
// ...
}
}
Эта конфигурация определяет маршрут под названием blog_list
, который совпадает,
когда пользователь запрашивает URL /blog
. Когда происходит совпадение, приложение
выполняет метод list()
класса BlogController
.
Note
Строка запроса URL не рассматривается при сопоставлении маршрутов. В этом
примере, URL вроде /blog?foo=bar
и /blog?foo=bar&bar=foo
будут так же
совпадать с маршрутом blog_list
.
Caution
Если вы определяете несколько PHP-классов в одном файле, Symfony загружает только маршруты первого класса, игнорируя все другие.
Имя маршрута (blog_list
) сейчас не важно, но будет иметь значение позже, когда
вы будете генерировать URL . Вам нужно только иметь
в виду, что каждое имя маршрута должно быть уникальным в приложении.
Создание маршрутов в файлах YAML, XML или PHP
Вместо определения маршрутов в классах контроллера, вы можете определять их в отдельном файле YAML, XML или PHP. Главное преимущество - они не будут требовать никаких дополнительных зависимостей. Главный недостаток - вам нужно работать с несколькими файлами при проверке маршрутизации какого-то действия контроллера.
Следующий пример показывает, как определять в YAML/XML/PHP маршрут под названием
blog_list
, который ассоциирует URL /blog
с действием list()
BlogController
:
1 2 3 4 5 6 7 8 9
# config/routes.yaml
blog_list:
path: /blog
# значение контроллера ммеет формат 'controller_class::method_name'
controller: App\Controller\BlogController::list
# еслм действие реализуется как метод __invoke() класса контроллера,
# вы можете пропустить часть '::method_name':
# controller: App\Controller\BlogController
Note
По умолчанию Symfony загружает только маршруты, определенные в
формате YAML. Если вы определяете маршруты в формате XML и/или PHP,
обновите файл src/Kernel.php .
Сопоставление HTTP-методов
По умолчанию, маршруты совпадают с любым глаголом HTTP (GET
, POST
, PUT
, и др.).
Используйте опцию methods
, чтобы ограничить глаголы, на которые долшжен реагировать каждый маршрут:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// src/Controller/BlogApiController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class BlogApiController extends AbstractController
{
#[Route('/api/posts/{id}', methods: ['GET', 'HEAD'])]
public function show(int $id): Response
{
// ... вернуть JSON-ответ с постом
}
#[Route('/api/posts/{id}', methods: ['PUT'])]
public function edit(int $id): Response
{
// ... отредактировать пост
}
}
Tip
HTML-формы поддерживают только методы GET
и POST
. Если вы вызываете маршрут с
другим методом из HTML-формы, добавьте скрытое поле под названием _method
с методом
для использования (например, <input type="hidden" name="_method" value="PUT"/>
).
Если вы создаете ваши формы с помощью Форм Symfony это делается за вас
автоматически, когда опция framework.http_method_override
- true
.
Сопоставление выражений
Используйте опцию condition
, если вам нужно, чтобы какой-то маршрут совпадал,
основываясь на некоторой произвольной логике совпадения:
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 27 28 29 30 31 32
// src/Controller/DefaultController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class DefaultController extends AbstractController
{
#[Route(
'/contact',
name: 'contact',
condition: "context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'",
// выражения также могут включать в себя параметры конфигурации:
// condition: "request.headers.get('User-Agent') matches '%app.allowed_browsers%'"
)]
public function contact(): Response
{
// ...
}
#[Route(
'/posts/{id}',
name: 'post_show',
// выражения могут извлекать значения параметра маршрута, используя переменную "params"
condition: "params['id'] < 1000"
)]
public function showPost(int $id): Response
{
// ... вернуть ответ JSON с постом
}
}
Значение опции condition
- это любое валдиное
выражение ExpressionLanguage
и может использовать любую из этих переменных, созданных Symfony:
context
- Экземпляр RequestContext, который содержит наиболее фунламентальную информацию о сопоставляемом маршруте.
request
- Объект Запроса Symfony , который представляет текущий запрос.
params
- Массив совпадающих параметров маршрута для текущего маршрута.
Вы также можете использовать эти функции:
env(string $name)
- Возвращает значение переменной, используя Процессоры переменных окружения
service(string $alias)
-
Возвращает сервис условий маршрутизации.
Для начала, добавьте атрибут
#[AsRoutingConditionService]
или тегrouting.condition_service
к сервисам, которые вы хотите использовать в условиях маршрута:1 2 3 4 5 6 7 8 9 10 11
use Symfony\Bundle\FrameworkBundle\Routing\Attribute\AsRoutingConditionService; use Symfony\Component\HttpFoundation\Request; #[AsRoutingConditionService(alias: 'route_checker')] class RouteChecker { public function check(Request $request): bool { // ... } }
Затем, используйте функцию
service()
, чтобы сослаться на этот сервис внутри условий:1 2 3 4
// Controller (using an alias): #[Route(condition: "service('route_checker').check(request)")] // Or without alias: #[Route(condition: "service('Ap\\\Service\\\RouteChecker').check(request)")]
За кулисами. выражения компилируются в чистый PHP. Из-за этого, использование ключа
condition
не вызывает дополнительной нагрузки кроме времени, необходимого для
выполнения низлежащего PHP.
Caution
Условия не берутся во внимание при генерировании URL (что объясняется позже в этой статье).
Отладка маршрутов
По мере роста вашего приложения, у вас в итоге будет много маршрутов. Symfony включает
в себя несколько команд, чтобы помочь вам с отладкой проблем маршрутизации. Для начала,
команда debug:router
перечисляет все маршруты вашего приложения в том же порядке, в
котором их оценивает Symfony:
1 2 3 4 5 6 7 8 9 10 11 12
$ php bin/console debug:router
---------------- ------- ------- ----- --------------------------------------------
Имя Метод Схема Хост Путь
---------------- ------- ------- ----- --------------------------------------------
homepage ANY ANY ANY /
contact GET ANY ANY /contact
contact_process POST ANY ANY /contact
article_show ANY ANY ANY /articles/{_locale}/{year}/{title}.{_format}
blog ANY ANY ANY /blog/{page}
blog_show ANY ANY ANY /blog/{slug}
---------------- ------- ------- ----- --------------------------------------------
Передайте имя (или его часть) какого-то маршрута этому аргументу, чтобы отобразить детали маршрута:
1 2 3 4 5 6 7 8 9 10 11
$ php bin/console debug:router app_lucky_number
+--------------+---------------------------------------------------------+
| Свойство | Значение |
+--------------+---------------------------------------------------------+
| Имя маршрута | app_lucky_number |
| Путь | /lucky/number/{max} |
| ... | ... |
| Опции | compiler_class: Symfony\Component\Routing\RouteCompiler |
| | utf8: true |
+--------------+---------------------------------------------------------+
Tip
Используйте опцию --show-aliases
, чтобы показать все доступные псевдонимы
для заданного маршрута.
Другая команда называется router:match
и она показывает, какой маршрут будет
совпадать с заданным URL. It's useful to find out why some URL is not executing the
controller action that you expect:
1 2 3
$ php bin/console router:match /lucky/number/8
[OK] Route "app_lucky_number" совпадает
Параметры маршрута
Предыдущие примеры определяют маршруты, где URL никогда не изменяется (например,
/blog
). Однако, часто определяют маршруты, где какая-то часть - переменная.
Например, URL для отображения какого-то поста блога скорее всего будет включать в
себя название или слаг (например, /blog/my-first-post
или /blog/all-about-symfony
).
В маршрутах Symfony, переменные части заключены в { }
.
Например, маршрут для отображения содержания поста блога,
определяется как /blog/{slug}
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// src/Controller/BlogController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class BlogController extends AbstractController
{
// ...
#[Route('/blog/{slug}', name: 'blog_show')]
public function show(string $slug): Response
{
// $slug будет равен динамической части URL
// например, /blog/yay-routing, затем $slug='yay-routing'
// ...
}
}
Имя переменной части ({slug}
в этом примере) используется для создания PHP-переменной,
где хранится содержание этого маршрута и передается контроллеру. Если пользователь посещает
URL /blog/my-first-post
, Symfony выполняет метод show()
в классе BlogController
,
и передает аргумент $slug = 'my-first-post'
методу show()
.
Маршруты могут определять любое количество параметров, но каждый из них может быть
использовать только единожды в каждом маршруте (например, /blog/posts-about-{category}/page/{pageNumber}
).
Валидация параметров
Представьте, что ваше приложение имеет маршрут blog_show
(URL: /blog/{slug}
)
и маршрут blog_list
(URL: /blog/{page}
). Учитывая, что параметры маршрута принимают
любые значения, нет возможности дифферинциировать эти два маршрута.
Если пользователь запрашивает /blog/my-first-post
, оба маршрута совпадут, и Symfony
будет использовать маршрут, который был определен первым. Чтобы исправить это, добавьте
некоторую валидацию к параметру {page}
, используя опцию requirements
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// src/Controller/BlogController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class BlogController extends AbstractController
{
#[Route('/blog/{page}', name: 'blog_list', requirements: ['page' => '\d+'])]
public function list(int $page): Response
{
// ...
}
#[Route('/blog/{slug}', name: 'blog_show')]
public function show($slug): Response
{
// ...
}
}
Опция requirements
определяет регулярные PHP-выражения, которым должны
соответствовать параметры маршрута для того, чтобы совпадал весь маршрут. В этом
примере, \d+
- это регулярное выражение, которое совпадает с однозначным числом
любой длины. Теперь:
URL | ??????? | ????????? |
---|---|---|
/blog/2 |
blog_list |
$page = 2 |
/blog/my-first-post |
blog_show |
$slug = my-first-post |
Tip
Перечисление Requirement содержит коллекцию часто используемых констант регулярных выражений, таких как цифры, даты и UUID, которые могут быть использованы как требования параметров маршрута.
Tip
Требования маршрута (и путей маршрута) могут включать в себя параметры контейнера , что полезно для определения сложных регулярных выражений единожды и повторного их использования во многих маршрутах.
Tip
Параметры также поддерживают свойства PCRE Unicode, которые являются
последовательностями экранирования, совпадаюшими с общими типами символов.
Например, \p{Lu}
совпадает с любым заглавным символом на любом языке,
\p{Greek}
совпадает с любым греческим символом и т.д.
Note
При использовании регулярных выражений в параметрах маршрута, вы можете установить
опцию маршрута utf8
как true
, чтобы сделать так, чтобы любой символ .
совпадал с любым символом UTF-8, а не только с одним битом.
Если вы хотите, требования можно встроить в каждый параметр, используя синтаксис
{parameter_name<requirements>}
. Эта функция делает конфигурацию более компактной,
но может уменьшить читаемость маршрута, если требования сложные:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/Controller/BlogController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class BlogController extends AbstractController
{
#[Route('/blog/{page<\d+>}', name: 'blog_list')]
public function list(int $page): Response
{
// ...
}
}
Необязательные параметры
В предыдущем примере, URL blog_list
- /blog/{page}
. Если пользователи
посещают /blog/1
, он будет совпадать. Но если они посетят /blog
, он не
будет совпадать. Как только вы добавите к маршруту параметр, он должен иметь значение.
Вы также можете сделать так, чтобы blog_list
снова совпадал, когда пользователь
посещает /blog
, добавив значение по умолчанию к параметру {page}
. При использовании
аннотаций, значения по умолчанию определяются в аргумнтах действия контроллера. В других
форматах конфигурации они определяются опцией defaults
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/Controller/BlogController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class BlogController extends AbstractController
{
#[Route('/blog/{page}', name: 'blog_list', requirements: ['page' => '\d+'])]
public function list(int $page = 1): Response
{
// ...
}
}
Теперь, когда пользователь посещает /blog
, маршрут blog_list
будет совпадать,
а $page
по умолчанию будет иметь значение 1
.
Caution
Вы можете иметь более одного необязательного параметра (например, /blog/{slug}/{page}
),
но все после необязательного параметра должно быть необязательно. Например,
/{page}/blog
- это валидный путь, но page
всегда будет обязательным
(т.е. /blog
не будет совпадать с этим маршрутом).
Если вы хотите всегда включать какое-то значение по умолчанию в сгенерированном URL (например,
для генерирования /blog/1
вместо /blog
в предыдущем примере), добавьте символ !
перед именем параметра: /blog/{!page}
Как это происходит с требованиями, значения по умолчанию также могут быть
встроены в каждый параметр, используя синтаксис {parameter_name?default_value}
.
Эта функция совместима со встроенными требованиями, поэтому вы можете встроить
обе в один параметр:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/Controller/BlogController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class BlogController extends AbstractController
{
#[Route('/blog/{page<\d+>?1}', name: 'blog_list')]
public function list(int $page): Response
{
// ...
}
}
Tip
Чтобы дать значение по умолчанию null
любому параметру, ничего не
добавляйте после символа ?
(например, /blog/{page?}
). Если вы так сделаете,
не забудьте обновить типы связанных аргументов контроллера, чтобы позволить передачу
значений null
(например, замените int $page
на ?int $page
)
Параметр приоритетности
Symfony оценивает маршруты в порядке, котором они определены. Если путь маршрута
совпадает со многими разными паттернами, он может предотвратить другие маршруты от
совпадения. В YAML и XML вы можете перемещать определения маршрутов вверх и вниз в
файле конфигурации, чтобы контролировать их приоритетность. В маршрутах, определенных
как PHP-аннотации или атрибуты, это намного сложнее сделать, поэтому вы можете установить
необязательный параметр priority
в таких маршрутах, чтобы контролировать их приоритетность:
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/Controller/BlogController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Attribute\Route;
class BlogController extends AbstractController
{
/**
* Этот маршрут имеет жадный паттерн и определяется первым.
*/
#[Route('/blog/{slug}', name: 'blog_show')]
public function show(string $slug): Response
{
// ...
}
/**
* Этот маршрут не может быть сопоставлен без определения приоритета выше, чем 0.
*/
#[Route('/blog/list', name: 'blog_list', priority: 2)]
public function list(): Response
{
// ...
}
}
Параметр приоритета ожидает целое значение. Маршруты с более высоким приоритетом
сортируются до маршрутов с более низким приоритетом. Значение по умолчанию, если
параметр не определен, - 0
.
Конверсия параметров
Распространенной потребностью маршрутизации является конверсия значения, хранящегося в некотором параметре (например, целое число, действующее, как ID пользователя), в другое значение (например, объект, представляющий пользователя). Эта функция называется "param converter".
Чтобы добавить поддержку "param converters", нам нужен SensioFrameworkExtraBundle:
1
$ composer require sensio/framework-extra-bundle
Теперь сохраните предыдущую конфигурацию маршрута, но измените аргументы
действия контроллера. Вместо string$slug
добавьте BlogPost $post
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// src/Controller/BlogController.php
namespace App\Controller;
use App\Entity\BlogPost;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class BlogController extends AbstractController
{
// ...
#[Route('/blog/{slug}', name: 'blog_show')]
public function show(BlogPost $post): Response
{
// $post это объект, чей слаг сооветствует параметру маршрутизации
// ...
}
}
Если ваши аргументы контроллера включают в себя подсказки для объектов (BlogPost
в этом случае), "param converter" делает запрос в базу данных, чтобы найти объект,
использующий параметры запроса (slug
в этом случае). Если объект не найден,
Symfony автоматически генерирует ответ 404.
Прочтите документацию Doctrine по преобразованию параметров ,
чтобы узнать об атрибуте #[MapEntity]
, который может быть использован для настройки
запросов базы данных, используемых для получения объектов из параметра маршрутов.
Подкрепленные параметры исчисления
В качестве параметров маршрута можно использовать подкрепленные исчисления PHP, поскольку Symfony
автоматически преобразует их в скалярные значения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// src/Controller/OrderController.php
namespace App\Controller;
use App\Enum\OrderStatusEnum;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class OrderController extends AbstractController
{
#[Route('/orders/list/{status}', name: 'list_orders_by_status')]
public function list(OrderStatusEnum $status = OrderStatusEnum::Paid): Response
{
// ...
}
}
Специальные параметры
В дополнение к вашим собственным параметрам, маршруты могут иметь любые следующие параметры, созданные Symfony:
_controller
- Этот параметр используется для определения того, какой контроллер и действие выполняется при совпадении маршрута.
_format
-
Совпавшее значение используется для установки "request format" объекта
Request
. Это используется для таких вещей, как установкаContent-Type
ответа (например, форматjson
переводится вContent-Type
дляapplication/json
). _fragment
-
Используется для установки идентификатора фрагмента, что является последней необязательной
частью URL, которая начинается с символа
#
и используется для идентификации части документа. _locale
- Используется для установки локали в запросе.
Вы можете добавить эти атрибуты (кроме _fragment
) как в индивидуальных маршрутах, так
и в импортированных. Symfony определяет некоторые особые атрибуты с одинаковым именем
(кроме нижнего подчеркивания в начале), поэтому вам может быть легче их определить:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// src/Controller/ArticleController.php
namespace App\Controller;
// ...
class ArticleController extends AbstractController
{
#[Route(
path: '/articles/{_locale}/search.{_format}',
locale: 'en',
format: 'html',
requirements: [
'_locale' => 'en|fr',
'_format' => 'html|xml',
],
)]
public function search(): Response
{
}
}
Дополнительные параметры
В опции маршрута defaults
вы можете по желанию определить параметры, не включенные
в конфигурацию маршрута. Это полезно для передачи дополнительных аргументов контроллерам
маршрутов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/Controller/BlogController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class BlogController extends AbstractController
{
#[Route('/blog/{page}', name: 'blog_index', defaults: ['page' => 1, 'title' => 'Hello world!'])]
public function index(int $page, string $title): Response
{
// ...
}
}
Символы слеша в параметрах маршрута
Параметры маршрута могут содержать любые значения, кроме символа слеша /
,
потому что этот символ используется для разделения разных частей URL. Например,
если значение token
в маршруте /share/{token}
содержит символ /
,
этот маршруте не будет совпадать.
Возможным решением будет изменение требований параметра, чтобы они были более вольными:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/Controller/DefaultController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class DefaultController extends AbstractController
{
#[Route('/share/{token}', name: 'share', requirements: ['token' => '.+'])]
public function share($token): Response
{
// ...
}
}
Note
Если маршрут определяет несколько параметров, и вы применяете это вольное
регулярное выражение ко всем, вы можете получить неожиданные результаты.
Например, если определение маршрута - /share/{path}/{token}
и как
path
, так и token
принимают /
. затем token
будет получать только последний
путь и остаток совпадения соотносится с path
.
Note
Если маршрут имеет специальный параметр {_format}
, вам не стоит использовать
требование .+
для параметров, позволяющих слеши. Например, если паттерн -
/share/{token}.{_format}
, а {token}
позволяет любые символы, URL
/share/foo/bar.json
будет рассматривать foo/bar.json
как токен, и формат
будет пустым. Это можно решить заменив требование .+
на [^.]+
, чтобы разрешить
любые символы, кроме точек.
Псевдонимы машрута
Псевдоним маршрута позволяет вам иметь несколько имен для одного маршрута:
1 2 3
# config/routes.yaml
new_route_name:
alias: original_route_name
В этом примере, и маршрут original_route_name
, маршрут new_route_name
могут
быть использованы в приложении, и приведут к одному результату.
Устаревание псевдонимов маршрута
Если какой-то псевдоним маршрута больше не стоит использовать (так как он устарел или вы решили больше его не поддерживать), вы можете объявить его определение устаревшим:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
new_route_name:
alias: original_route_name
# это выводит следующее общее сообщение об устаревании:
# Начиная с acme/package 1.2: Псевдоним маршрута "new_route_name" устарел. Вы должны перестать использовать его, так как он будет удален в будущем.
deprecated:
package: 'acme/package'
version: '1.2'
# вы также можете определить пользовательское сообщение об устаревании (доступен заполнитель %alias_id%)
deprecated:
package: 'acme/package'
version: '1.2'
message: 'Псевдоним маршрута "%alias_id%" устарел. Не используйте его больше.'
В этом примере, каждый раз, когда используется псевдоним new_route_name
, запускается
уведомление об устаревании, советующее вам перестать использовать этот псевдоним.
Сообщение на самом деле является шаблоном сообщения, который заменяет явления заполнителя
%alias_id%
именем псевдонима маршрута. У вас должно быть хотя бы одно явление
заполнителя %alias_id%
в вашем шаблоне.
Группы и префиксы маршрутов
Часто группа маршрутов будет иметь некоторые общие опции (например, все маршруты,
связанные с блогом, начинаются с /blog
). Поэтому Symfony имеет функцию общей
конфигурации маршрутов.
При определении маршрутов в виде атрибутов или аннотаций, поместите общую конфигурацию
в атрибут #[Route]
класса контроллера. В другим форматах
маршрутизации, определите общую конфигурацию, используя опции при импорте маршрутов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
// src/Controller/BlogController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
#[Route('/blog', requirements: ['_locale' => 'en|es|fr'], name: 'blog_')]
class BlogController extends AbstractController
{
#[Route('/{_locale}', name: 'index')]
public function index(): Response
{
// ...
}
#[Route('/{_locale}/posts/{slug}', name: 'show')]
public function show(string $slug): Response
{
// ...
}
}
В этом примере, маршрут действия index()
будет назван blog_index
и его URL будет
/blog/{_locale}
. Маршрут действия show()
будет назван blog_show
и его URL
будет /blog/{_locale}/posts/{slug}
. Оба маршрута также будут валидировать, что параметр
_locale
совпадает с регулярным выражением, определенным в аннотации класса.
Note
Если любой из маршрутов с префиксом определяет пустой путь, Symfony добавляет
к нему замыкающий слеш. В предыдущем примере, пустой путь с префиксом /blog
,
приведет к URL /blog/
. Если вы хотите избежать такого поведения, установите
опцию trailing_slash_on_root
как false
(эта опция недоступна при использовании
атрибутов или аннотаций PHP):
1 2 3 4 5 6 7
# config/routes/attributes.yaml
controllers:
resource: '../../src/Controller/'
type: attribute
prefix: '/blog'
trailing_slash_on_root: false
# ...
See also
Symfony может импортировать маршруты из других источников и вы можете даже создавать собственный загрузчик маршрутов.
Получение имени и параметров маршрута
Объект Request
, созданный Symfony хранит всю конфигурацию маршрута (такую как
имя и параметры) в "атрибутах запроса". Вы можете получить эту информацию в контроллере
через объект Request
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
// src/Controller/BlogController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class BlogController extends AbstractController
{
#[Route('/blog', name: 'blog_list')]
public function list(Request $request): Response
{
$routeName = $request->attributes->get('_route');
$routeParameters = $request->attributes->get('_route_params');
// используйте это, чтобы получить все доступные атрибуты (не только маршрутизации):
$allAttributes = $request->attributes->all();
// ...
}
}
В сервисах эту информацию можно получить
внедрив сервис RequestStack.
В шаблонах используйте глобальную переменную приложения Twig
для получения имени текущего маршрута (app.current_route
) и его параметров
(app.current_route_parameters
).
Специальные маршруты
Symfony определяет некоторые специальные контроллеры, чтобы отображать шаблоны и перенапрвлять на другие маршруты из конфигураци маршрута, чтобы вам не нужно было создавать новое действие контроллера.
Отображение шаблона прямо из маршрута
Прочтите раздел об отображении шаблона из маршрута в главной статье о шаблонах Symfony.
Перенапрвоение на URL и маршруты прямо из маршрута
Используйте RedirectController
, чтобы перенаправить на другие маршруты и URL:
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 27 28 29 30
# config/routes.yaml
doc_shortcut:
path: /doc
controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController
defaults:
route: 'doc_page'
# по желанию вы можете определить какие-то аргументы, переданные маршруту
page: 'index'
version: 'current'
# перенаправляния временные по умолчанию (код 302), но вы можете сделать их постоянными (код 301)
permanent: true
# добавьтье это, чтобы сохранить изначальные параметры строки запроса при перенаправлении
keepQueryParams: true
# добавьте это, чтобы оставить метод HTTP при перенаправлении. Статус перенаправления изменяется
# * для временных перенаправлений, используется статус-код 307 вместо 302
# * для постоянных перенаправлений, используется статус-код 307 вместо 301
keepRequestMethod: true
# добавьте это, чтобы удалить все исходные атрибуты маршрутов при перенаправлении
ignoreAttributes: true
# or specify which attributes to ignore:
# ignoreAttributes: ['offset', 'limit']
legacy_doc:
path: /legacy/doc
controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController
defaults:
# это значение может быть абсолютным путем или URL
path: 'https://legacy.example.com/doc'
permanent: true
Tip
Symfony также предоставляет некоторые утилиты для перенаправлений внутри контроллеров
Перенаправление URL с замыкающими слешами
Исторически, URL следовали соглашению UNIX о добавлении замыкающих слешей для каталогов
(например, https://example.com/foo/
) и их удаления для ссылания на файлы
(https://example.com/foo
). Хотя обслуживание разного содержания для обоих URL - это
ок, сейчас чащей всего оба URL будут рассматриваться как один и перенаправление меджу ними.
Symfony следует этой логике для перенаправления между URL с и без замыкающего слеша (но
только для запросов GET
и HEAD
):
??????? URL | ???? ??????????? URL /foo |
???? ??????????? URL /foo/ |
---|---|---|
/foo |
C???????? (??????-????? 200 ) |
?????? ??????????????? 301 ?? /foo |
/foo/ |
?????? ??????????????? 301 ?? /foo |
C???????? (??????-????? 200 ) |
Маршрутизация подкаталогов
Маршруты могут конфигурироать опцию host
, чтобы требовать, чтобы HTTP-хост входящих запросов
совпадал с некоторым конкретным значением. В следующем примере, оба маршрута совпадают с одним
путем (/
), но один из них отвечает только на определенное имя хоста:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// src/Controller/MainController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class MainController extends AbstractController
{
#[Route('/', name: 'mobile_homepage', host: 'm.example.com')]
public function mobileHomepage(): Response
{
// ...
}
#[Route('/', name: 'homepage')]
public function homepage(): Response
{
// ...
}
}
Значение опции host
может иметь параметры (что полезно в мультитенатных приложениях)
и эти параметры могут быть тоже валидированы с помощью requirements
:
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 27
// src/Controller/MainController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class MainController extends AbstractController
{
#[Route(
'/',
name: 'mobile_homepage',
host: '{subdomain}.example.com',
defaults: ['subdomain' => 'm'],
requirements: ['subdomain' => 'm|mobile'],
)]
public function mobileHomepage(): Response
{
// ...
}
#[Route('/', name: 'homepage')]
public function homepage(): Response
{
// ...
}
}
В примере выше, параметр subdomain
определяет значение по умолчанию, потому что иначе
вам нужно включать значение домена каждый раз, когда вы генерируете URL с использованием
этих маршрутов.
Tip
Вы также можете установить опцию host
, когда импортируете маршруты ,
чтобы сделать так, чтобы они все требовали это имя хоста.
Note
При использовании маршрутизации субкаталогов, вы должны устанавливать HTTP-заголовки
Host
в функциональных тестах, иначе маршруты не будут совпадать:
1 2 3 4 5 6 7 8 9
$crawler = $client->request(
'GET',
'/',
[],
[],
['HTTP_HOST' => 'm.example.com']
// или получить значение из какого-то параметра контейнера:
// ['HTTP_HOST' => 'm.' . $client->getContainer()->getParameter('domain')]
);
Tip
Вы также можете использовать встроенный формат по умолчаниюи и требования в опции
host
: {subdomain<m|mobile>?m}.example.com
Локализованные маршруты (i18n)
Если ваше приложение переводится на несколько языков, каждый маршрут может определять другой URL по каждой локали перевода . Это помогает избежать необходимости дублированния маршрутов, что также уменьшает потенциальные баги:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// src/Controller/CompanyController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class CompanyController extends AbstractController
{
#[Route(path: [
'en' => '/about-us',
'nl' => '/over-ons'
], name: 'about_us')]
public function about(): Response
{
// ...
}
}
Note
При использовании PHP-атрибутов для локализованных маршрутов, вам нужно использовать
параметр по имени path
, чтобы указать массив путей.
Когда совпадает локализованный маршрут, Symfony автоматически использует одну и ту же локаль во время всего запроса.
Tip
Когда приложение использует полные локали "язык + территория" (например, fr_FR
,
fr_BE
), если URL одинаковы во всех связанных локалях, маршруты могут спользовать
только часть языка (например, fr
), чтобы избежать повторения одних и тех же URL.
Распространенное требования для меджународных приложений - добавлять ко всем маршрутам префикс локали. Это может быть сделано путем определения разных префиксов для каждой локали (и установки пустого префикса для локали по умолчанию, если вы этого хотите):
1 2 3 4 5 6 7
# config/routes/annotations.yaml
controllers:
resource: '../../src/Controller/'
type: annotation
prefix:
en: '' # don't prefix URLs for English, the default locale
nl: '/nl'
Другое распространенное требование - размещать веб-сайт на разных доменах в соответствии с локалью. Это может быть сделано путем определения разных хостов для каждой локали.
1 2 3 4 5 6 7
# config/routes/attributes.yaml
controllers:
resource: '../../src/Controller/'
type: attribute
host:
en: 'www.example.com'
nl: 'www.example.nl'
Маршруты без состояния
Иногда, когда HTTP-ответ должен быть кеширован, важно убедиться, что это может произойти. Однако, каждый разm когда во время запроса начинается сессия, Symfony превращает ответ в частный некешируемый ответ.
Для деталей, см. HTTP-кеширование.
Маршруты могут конфигурировать булеву опцию stateless
, чтобы объявить, что сессия не
должна быть использована при сопоставлении с запросом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// src/Controller/MainController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Attribute\Route;
class MainController extends AbstractController
{
#[Route('/', name: 'homepage', stateless: true)]
public function homepage(): Response
{
// ...
}
}
Теперь, если сессия используется, приложение сообщит о ней, основываясь на вашем параметре
kernel.debug
:
enabled
: вызовет исключение UnexpectedSessionUsageExceptiondisabled
: запишет лог предупреждения
Это поможет вам понять, и, надеемся, исправить неожиданное поведение вашего приложения.
Генерирование URL
Системы маршрутизации двусторонни:
1. они ассоциируют URL с контроллерами (как
объясняется в предыдущих разделах);
2. они генерируют URL для заданного маршрута.
Генерирование URL из маршрутов позволяет вам не писать значения <a href="...">
вручную в ваших HTML-шаблонах. Также, если URL какого-то маршрута изменяется, вы
только должны обновить конфигурацию маршрута и все ссылки будут обновлены.
Чтобы сгенерировать URL, вам нужно указать имя маршрута (например, blog_show
)
и знчения параметров, определенных маршрутами (например, slug = my-blog-post
).
По этой причина каждый маршрут имеет внутреннее имя, которое должно быть уникально
в приложении. Если вы не установите имя маршрута ясно с опцией name
, Symfony
генерирует автоматическое имя, основываясь на контроллере и действии.
Symfony объявляет псевдонимы маршрутов на основе FQCN, если в целевом классе есть метод __invoke()
, который добавляет маршрут и если целевой класс добавил
только один маршрут. Symfony также автоматически добавляет псевдоним для каждого метода,
который определяет только один маршрут. Рассмотрим следующий класс:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// src/Controller/MainController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Attribute\Route;
final class MainController extends AbstractController
{
#[Route('/', name: 'homepage')]
public function homepage(): Response
{
// ...
}
}
Symfony добавит псевдоним маршрута под названием App\Controller\MainController::homepage
.
Генерирование URL в контроллерах
Если ваш контроллер расширяется из AbstractController ,
используйте помощник generateUrl()
:
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 27 28 29 30 31 32
// src/Controller/BlogController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class BlogController extends AbstractController
{
#[Route('/blog', name: 'blog_list')]
public function list(): Response
{
// сгенерируйте URL без аргументов маршрута
$signUpPage = $this->generateUrl('sign_up');
// сгенарируйте URL с аргументами марашрута
$userProfilePage = $this->generateUrl('user_profile', [
'username' => $user->getUserIdentifier(),
]);
// сгенерированные URL являются "абсолютными путями" по умолчанию. Передайте третий необязательный
// аргумент, чтобы сгенерировать другие URL (например, "абсолютный URL")
$signUpPage = $this->generateUrl('sign_up', [], UrlGeneratorInterface::ABSOLUTE_URL);
// когда маршрут локализован, Symfony по умолчанию использует текущую локаль запроса
// передайте другое знаениче '_locale', если вы хотите установить локаль ясно
$signUpPageInDutch = $this->generateUrl('sign_up', ['_locale' => 'nl']);
// ...
}
}
Note
Если вы передаетет методу generateUrl()
какие-то пааметры, которые не являются частью
определения маршрута, они включаются в сгенерированный URL как строка запроса:
1 2 3
$this->generateUrl('blog', ['page' => 2, 'category' => 'Symfony']);
// маршрут 'blog' определяет только параметр 'page'; сгенерированный URL:
// /blog/2?category=Symfony
Caution
В то время как объекты преобразуются в строку при использовании в качестве заполнителей, они не преобразуются при использовании в качестве дополнительных параметров. Поэтому, если вы передаете объект (например, Uuid) в качестве значения дополнительного параметра, вам нужно ясно преобразовать его в строку:
1
$this->generateUrl('blog', ['uuid' => (string) $entity->getUuid()]);
Если ваш контроллер не расширяется из AbstractController
, вам понадобится
извлечь сервисы в вашем контроллере и следовать
инструции в следующем разделе.
Генерирование URL в сервисах
Внедрите сервис Symfony router
в ваши собственные сервисы и используйте его метод
generate()
. При использовании автомонтирования сервисов,
вам понадобится только добавить аргумент в конструктор сервиса и типизировать его классом
UrlGeneratorInterface:
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 27 28 29 30 31 32 33
// src/Service/SomeService.php
namespace App\Service;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class SomeService
{
public function __construct(
private UrlGeneratorInterface $router,
) {
}
public function someMethod(): void
{
// ...
// сгенерируйте URL без аргументов маршрута
$signUpPage = $this->router->generate('sign_up');
// сгенерируйте URL с аргументами маршрута
$userProfilePage = $this->router->generate('user_profile', [
'username' => $user->getUserIdentifier(),
]);
// сгенерированные URL являются "абсолютными путями" по умолчанию. Передайте третий необязательный
// аргумент, чтобы сгенерировать другие URL (например, "абсолютный URL")
$signUpPage = $this->router->generate('sign_up', [], UrlGeneratorInterface::ABSOLUTE_URL);
// когда маршрут локализован, Symfony по умолчанию использует текущую локаль запроса
// передайте другое знаениче '_locale', если вы хотите установить локаль ясно
$signUpPageInDutch = $this->router->generate('sign_up', ['_locale' => 'nl']);
}
}
Генерирование URL в шаблонах
Прочтите раздел о создании ссылок между страницами в главной статье о шаблонах Symfony.
Генерирование URL в JavaScript
Если ваш код JavaScript включен в шаблон Twig, вы можете использовать функции Twig
path()
и url()
, чтобы сгенрировать URL и сохранить их в переменных JavaScript.
Фильтр escape()
необходим для экранирования любых небезопасных для JavaScript значений:
1 2 3
<script>
const route = "{{ path('blog_show', {slug: 'my-blog-post'})|escape('js') }}";
</script>
Если вам нужно сгенерировать URL динамически или если вы используете чистый код JavaScript, это решение не работает. В таких случаях рассмотрите использование FOSJsRoutingBundle.
Генерирование URL в командах
Генерирование URL в командах работает так же, как
генерирование URL в сервисах . Единственная
разница в том, что команды не выполняются в HTTP-контексте. Следовательно, если вы генерируете
абсолютные URL, вы получите http://localhost/
в качестве имени хоста вместо вашего реального
имени хоста.
Решением будет сконфигурировать опцию default_uri
, чтобы определить "контекст запроса",
используемы командами, когда они генерируют URL:
1 2 3 4 5
# config/packages/routing.yaml
framework:
router:
# ...
default_uri: 'https://example.org/my/path/'
Now you'll get the expected results when generating URLs in your commands:
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 27 28 29 30 31 32 33 34 35 36 37
// src/Command/SomeCommand.php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
// ...
class SomeCommand extends Command
{
public function __construct(private UrlGeneratorInterface $urlGenerator)
{
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
// сгенерируйте URL без аргументов маршрута
$signUpPage = $this->router->generate('sign_up');
// сгенерируйте URL с аргументами маршрута
$userProfilePage = $this->router->generate('user_profile', [
'username' => $user->getUserIdentifier(),
]);
// сгенерированные URL являются "абсолютными путями" по умолчанию. Передайте третий необязательный
// аргумент, чтобы сгенерировать другие URL (например, "абсолютный URL")
$signUpPage = $this->router->generate('sign_up', [], UrlGeneratorInterface::ABSOLUTE_URL);
// когда маршрут локализован, Symfony по умолчанию использует текущую локаль запроса
// передайте другое знаениче '_locale', если вы хотите установить локаль ясно
$signUpPageInDutch = $this->router->generate('sign_up', ['_locale' => 'nl']);
// ...
}
}
Note
По умолчанию, URL, сгенерированные для веб-ресурсов, используют одно и то же значение
default_uri
, но вы можете изменить это с помощью параметров контейнера
asset.request_context.base_path
и asset.request_context.secure
.
Проверка существования маршрута
В высокодинамичных приложениях может быть необходимо проверить, существует ли маршрут, до его использования для генерирования URL. В этих случаях, не используйте метод getRouteCollection(), потому что это регенерирует кеш маршрутизации и замедляет приложение.
Вместо этого, попоробуйте сгенрировать URL и поймать RouteNotFoundException, вызванное, когда маршрут не существует:
1 2 3 4 5 6 7 8 9
use Symfony\Component\Routing\Exception\RouteNotFoundException;
// ...
try {
$url = $this->router->generate($routeName, $routeParameters);
} catch (RouteNotFoundException $e) {
// маршрут не определен...
}
Форсирование HTTPS в сгенерированных URL
По умолчанию, сгенерированные URL используют ту же HTTP-схему, что и текущий
запрос. В консольных командах, где нет HTTP-запроса, URL используют http
по умолчанию. Вы можете изменить это для каждой команды (через метод маршрутизатора
getContext()
) или глобально со следующими параметрами конфигурации:
1 2 3 4
# config/services.yaml
parameters:
router.request_context.scheme: 'https'
asset.request_context.secure: true
Вне конспольных команд, используйте опцию schemes
, чтобы ясно определить схему
каждого маршрута:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/Controller/SecurityController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class SecurityController extends AbstractController
{
#[Route('/login', name: 'login', schemes: ['https'])]
public function login(): Response
{
// ...
}
}
URL, сгенерированный для маршрута login
всегда будет использовать HTTPS. Это означает,
что при использовании функции Twig path()
для генерирования URL, вы можете получить абсолютный
URL вместо относительного, если HTTP-схема изнчального запроса отличается от схемы, используемой
маршрутом:
1 2 3 4 5 6
{# если текущая схема - HTTPS, генерирует относительный URL: /login #}
{{ path('login') }}
{# если текущая схема - HTTP, генерирует абсолютный URL для изменения
схемы: https://example.com/login #}
{{ path('login') }}
Требование схемы также форсируется для входящих запросов. Если вы попробуете получить
дотуп к URL /login
с HTTP, вы автоматически будете перенаправлены на тот же URL,
но с HTTPS-схемой.
Если вы хотите заставить группу маршрутов использовать HTTPS, вы можете определить схему по умолчанию при их импортировании. Следующий пример форсирует HTTPS на все маршруты, определенные как аннотации:
1 2 3 4 5
# config/routes/attributes.yaml
controllers:
resource: '../../src/Controller/'
type: attribute
schemes: [https]
Note
Компонент Security предоставляет
другой способ форсирования HTTP или HTTPS
через настройку requires_channel
.
Подписание URI
Подписанный URI - это URI, содержащий хеш-значение, которое зависит от содержания
URI. Таким образом, впоследствии можно проверить целостность подписанного URI,
повторно вычислив его хеш-значение и сравнив его с хешем, включенным в URI.
Symfony предоставляет утилиту для подписания URI с помощью сервиса UriSigner, который вы можете внедрить в свои сервисы или контроллеры:
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 27 28 29 30 31 32
// src/Service/SomeService.php
namespace App\Service;
use Symfony\Component\HttpFoundation\UriSigner;
class SomeService
{
public function __construct(
private UriSigner $uriSigner,
) {
}
public function someMethod(): void
{
// ...
// сгенерировать URL самостоятельно или получить его каким-то образом...
$url = 'https://example.com/foo/bar?sort=desc';
// подписать URL (добавляет параметр запроса под названием '_hash')
$signedUrl = $this->uriSigner->sign($url);
// $url = 'https://example.com/foo/bar?sort=desc&_hash=e4a21b9'
// проверить подпись URL
$uriSignatureIsValid = $this->uriSigner->check($signedUrl);
// $uriSignatureIsValid = true
// если у вас есть доступ к текущему объекту Request, вы можете использовать этот
// другой метод, чтобы передать весь объект Request вместо URI:
$uriSignatureIsValid = $this->uriSigner->checkRequest($request);
}
}
По соображениям безопасности обычно принято заставлять подписанные URI заканчивать свое
действие через некоторое время (например, при использовании их для сброса учетных данных
пользователя). По умолчанию подписанные URI не заканчивают свое действие, но вы можете
задать дату/время истечения срока действия, используя $expiration
аргумента
Symfony\\Component\\HttpFoundation\\UriSigner::sign():
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 27 28 29 30 31 32
// src/Service/SomeService.php
namespace App\Service;
use Symfony\Component\HttpFoundation\UriSigner;
class SomeService
{
public function __construct(
private UriSigner $uriSigner,
) {
}
public function someMethod(): void
{
// ...
// сгенерировать URL самостоятельно или получить его каким-то образом...
$url = 'https://example.com/foo/bar?sort=desc';
// подписать URL с явной датой истечения срока
$signedUrl = $this->uriSigner->sign($url, new \DateTimeImmutable('2050-01-01'));
// $signedUrl = 'https://example.com/foo/bar?sort=desc&_expiration=2524608000&_hash=e4a21b9'
// если вы передадите \DateInterval, теперь он будет добавляться для получения даты истечения срока
$signedUrl = $this->uriSigner->sign($url, new \DateInterval('PT10S')); // валидный в течение 10 от сейчас
// $signedUrl = 'https://example.com/foo/bar?sort=desc&_expiration=1712414278&_hash=e4a21b9'
// вы также можете использовать временную метку в секундах
$signedUrl = $this->uriSigner->sign($url, 4070908800); // временная метка для даты 2099-01-01
// $signedUrl = 'https://example.com/foo/bar?sort=desc&_expiration=4070908800&_hash=e4a21b9'
}
}
Note
Дата/время истечения срока действия включается в подписанные URI в виде временной метки через
параметр запроса _expiration
.
7.1
Функция добавления даты истечения срока действия для подписанного URI была представлена Symfony 7.1.
Отладка проблем
Вот некоторые распространенные ошибки, которые вы можете увидеть при работе с маршрутизацией:
1 2
Controller "App\\Controller\\BlogController::show()" требует, чтобы вы
предоставили значение для аргумента "$slug".
Это происходит, когда ваш метод контроллера имеет аргумент (например, $slug
):
1 2 3 4
public function show(string $slug): Response
{
// ...
}
Но ваш путь маршруте не имеет параметра {slug}
(например, он /blog/show
).
Добавьте {slug}
к вашему пути маршрута: /blog/show/{slug}
или дайте аргументу
значение по умолчанию (т.e. $slug = null
).
1 2
Некоторые обязательные параметры не имеют ("slug") для генерирования URL для маршрута
"blog_show".
Это означает, что вы пробуете сгенерировать URL к маршруту blog_show
, но не
передаете значение slug
(что обязательно, так как оно имеет параметр {slug}
в пути маршрута). Чтобы исправить это, передайте значение slug
при генерировании
маршрута:
1
$this->generateUrl('blog_show', ['slug' => 'slug-value']);
или, в Twig:
1
{{ path('blog_show', {slug: 'slug-value'}) }}
Узнайте больше о маршрутизации
- Как ограничить сопоставление маршрутов с помощью условий
- Как создать пользовательский загрузчик маршрутов
- Как визуализиовать и отлаживать маршруты
- Как подключать внешние источники маршрутизации
- Как передать дополнительную информацию из маршрута в контроллер
- Как генерировать URL маршрутизации в JavaScript
- Как сделать так, чтобы маршрут соответствовал на основании хоста
- Как определять необязательные заполнители
- Как сконфигурировать перенаправление без пользовательского контроллера
- Перенаправление URL с замыкающим слешем
- Как определять требования маршрутов
- Поиск маршрутов из базы данных: Symfony CMF DynamicRouter
- Как заставить маршруты всегда использовать HTTPS или HTTP
- Как использовать параметры сервис-контейнера в ваших маршрутах
- Как позволить знак "/" в параметре маршрута