symfony Разрешить HTTP OPTIONS для набора (или всех) методов (без бандла). Обработка события получения запроса kernel.request
Primary tabs
Проверено в Symfony 4.3:
Предлагаемое решение состоит в том, чтобы поставить подобный обработчик события, в дикторию:
# src/EventListener/RequestListener.php
namespace App\EventListener;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use App\Security\CorsSettings;
use Symfony\Component\HttpFoundation\JsonResponse;
class RequestListener
{
public function onKernelRequest(GetResponseEvent $event)
{
if ($event->getRequest()->getMethod() === 'OPTIONS') {
$headers = CorsSettings::getHeadres();
$response = new JsonResponse([
'method' => $event->getRequest()->getMethod(),
], 200, $headers);
$event->setResponse($response);
}
}
}где:
- CorsSettings::getHeadres() -- вернет массив нужных заголовков (у вас может быть другая реализация)
- $event->getRequest() -- получив запросы вы можете также узнать и маршрут (если требуется разрешить что-то только для отдельных маршрутов)
далее в:
config/services.yaml
добавим:
app.options_listener:
class: App\EventListener\RequestListener
tags:
- { name: kernel.event_listener, event: kernel.request, priority: 33 }
Чтобы узнать нужный приоритет, можно использовать команду:
bin/console debug:event-dispatcher kernel.request
-- в нашем случае нам нужно иметь приоритет ниже (чтобы выполняться позже) чем RouterListener::onKernelRequest() (на момент написания этих строк в симфони 7 у него 32 приоритет)
Без конфигурации в services.yaml
Аналогичный функционал можно получить и без правки config/services.yaml, если
в стандартную автозагружаемую директорию:
src/EventSubscriber
добавить класс:
// src/EventSubscriber/TokenSubscriber.php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpFoundation\JsonResponse;
use App\Security\CorsSettings;
class TokenSubscriber implements EventSubscriberInterface
{
public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
if ($request->getMethod() == 'OPTIONS') {
$event->setResponse( // изменяем ответ
new JsonResponse('', 200, CorsSettings::getHeadres())
);
}
}
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => ['onKernelRequest', 2049],
];
}
}Спасибо за решение: @math2
Источники:
Вариант для установки CORS для OPTIONS (не из контроллера) и других ответов (из контроллера)
Такой вариант решает проблему корс в целом для приложения (не только для Options, но и для ответов контроллеров):
<?php
namespace App\EventListener;
use App\Security\CorsSettings;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
final class CorsEventListener
{
private const OPTIONS_NAME = 'OPTIONS';
protected string $lastRequestMethod = '';
public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
$this->lastRequestMethod = strtoupper($request->getMethod());
if ($this->lastRequestMethod === self::OPTIONS_NAME) {
$response = new Response('', 204);
$response->headers->add(CorsSettings::getOptionsHeaders());
$event->setResponse($response);
}
}
/**
* @see symfony.com/doc/current/event_dispatcher.html
* @see symfony.com/doc/current/reference/events.html
* @see stackoverflow.com/a/59715685
*/
public function onKernelResponse(ResponseEvent $event): void
{
// Для OPTIONS все настройки уже установлены в onKernelRequest()
if ($this->lastRequestMethod !== self::OPTIONS_NAME) {
$response = $event->getResponse();
$response->headers->add(CorsSettings::getHeaders());
}
}
}
-- тут для OPTIONS мы перехватываем request, а для остальных методов - response
и в config/services.yaml:
App\EventListener\CorsEventListener:
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 33 }
- { name: kernel.event_listener, event: kernel.response, method: onKernelResponse }
А также возможная реализация CorsSettings:
<?php
namespace App\Security;
use ItForFree\rusphp\Network\Url;
/**
* Настройки для CORS
*/
class CorsSettings
{
const ACCESS_CONTROL_ALLOW_METHODS = 'GET, PUT, POST, DELETE, OPTIONS';
/**
* Заголовки для поддержки кросс-доменных запросов
*
* @return array
*/
public static function getOptionsHeaders()
{
$headers = [
'Access-Control-Allow-Origin' => self::getAccessControlOriginValue(),
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Allow-Methods' => self::ACCESS_CONTROL_ALLOW_METHODS,
'Access-Control-Max-Age' => 1,
'Access-Control-Allow-Headers' => 'Content-Type, Content-Range, Content-Disposition, Content-Description, X-AUTH-TOKEN, Set-Cookie'
];
return $headers;
}
/**
* Заголовки для поддержки кросс-доменных запросов
*
* @return array
*/
public static function getHeaders()
{
$headers = [
'Access-Control-Allow-Origin' => self::getAccessControlOriginValue(),
'Access-Control-Allow-Credentials' => 'true',
];
return $headers;
}
protected static function getAccessControlOriginValue()
{
$allowedOrigins = ''; // по умолчанию запрещаем с других доменов
$origin = !empty($_SERVER['HTTP_ORIGIN']) ?
$_SERVER['HTTP_ORIGIN'] : '';
if (($_SERVER['APP_ENV'] == 'dev')
&& !empty($origin)) {
$allowedOrigins = $origin;
}
return $allowedOrigins;
}
}
Материалы по теме
- Видео: CORS в Symfony для OPTIONS и других HTTP запросов/ответов:
- Log in to post comments
- 2728 reads
vedro-compota
Mon, 06/02/2025 - 16:05
Permalink
О выяснении приоретата
О выяснении приоретата обработчиков событий см. тут https://fkn.ktu10.com/?q=node/10983
_____________
матфак вгу и остальная классика =)
vedro-compota
Thu, 06/19/2025 - 02:19
Permalink
Можно перехватывать и событие
Можно перехватывать и событие запроса и событие ответа:
<?php namespace App\EventListener; use App\Security\CorsSettings; use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\RequestEvent; final class CoreEventListener { private const OPTIONS_NAME = 'OPTIONS'; protected string $lastRequestMethod = ''; public function onKernelRequest(RequestEvent $event) { $request = $event->getRequest(); $this->lastRequestMethod = strtoupper($request->getMethod()); if ($this->lastRequestMethod === self::OPTIONS_NAME) { $response = new Response('', 204); $response->headers->add(CorsSettings::getOptionsHeaders()); $event->setResponse($response); } } /** * @see https://symfony.com/doc/current/event_di... * @see https://symfony.com/doc/current/referenc... * @see https://stackoverflow.com/a/59715685 */ public function onKernelResponse(ResponseEvent $event): void { if ($this->lastRequestMethod === self::OPTIONS_NAME) { $response = $event->getResponse(); $response->headers->add(CorsSettings::getHeaders()); // ... modify the response object } } }и в config/services.yaml:
# Register EventListener onKernelResponse App\EventListener\CoreEventListener: tags: - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 2049 } # - { name: kernel.event_listener, event: kernel.response, method: onKernelResponse }-- работает в symfony 7
_____________
матфак вгу и остальная классика =)