Компонент HttpFoundation
Дата обновления перевода: 2024-07-03
Компонент HttpFoundation
Компонент HttpFoundation определяет объектно-ориентированный слой спецификации HTTP.
В PHP, запрос представлен некоторыми глобальными переменными ($_GET
,
$_POST
, $_FILES
, $_COOKIE
, $_SESSION
, ...), а ответ генерируется
некоторыми функциям (echo
, header()
, setcookie()
, ...).
Компонент Symfony HttpFoundation заменяет эти глобальные переменные и функции объектно-ориентироанным слоем.
Установка
1
$ composer require symfony/http-kernel
Note
Если вы устанавливаете этот компонент вне приложения Symfony, вам нужно
подключить файл vendor/autoload.php
в вашем коде для включения механизма
автозагрузки классов, предоставляемых Composer. Детальнее читайте в
этой статье.
See also
Эта статья объясняет как использовать функции HttpFoundation как независимого компонента в любом приложении PHP. В приложениях Symfony уже всё настроено и готово к использованию. Прочитайте статью Контроллер для понимания как использовать эти функции при создании контроллеров.
Запрос
Наиболее распространённый способ создать запрос - основать его на текущих глобальных переменных PHP c createFromGlobals():
1 2 3
use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
Что почти эквивалентно более многословному, но также более гибкому, вызову __construct():
1 2 3 4 5 6 7 8
$request = new Request(
$_GET,
$_POST,
[],
$_COOKIE,
$_FILES,
$_SERVER
);
Доступ к данным запроса
Объект Запроса содержит информацию о запросе клиента. К этой информации можно получить доступ через несколько публичных свойств:
request
: эквивалент$_POST
;query
: эквивалент$_GET
($request->query->get('name')
);cookies
: эквивалент$_COOKIE
;attributes
: эквивалента нет - используется вашим приложением для хранения других данных (см. below );files
: эквивалент$_FILES
;server
: эквивалент$_SERVER
;headers
: наиболее эквивалентно субнабору$_SERVER
($request->headers->get('User-Agent')
).
Каждое свойство - это экземпляр ParameterBag (или его подкласс), с классом содержания данных:
request
: ParameterBag или InputBag, если данные исходят из параметров$_POST
;query
: ParameterBag;cookies
: InputBag;attributes
: ParameterBag;files
: FileBag;server
: ServerBag;headers
: HeaderBag.
Все экземпляры ParameterBag имеют методы для извлечения и обновления данных:
- all()
- Возвращает параметры.
- keys()
- Возвращает ключи параметра.
- replace()
- Заменяет текущие параметры новым набором.
- add()
- Добавляет параметры.
- get()
- Возвращает параметр по имени.
- set()
- Устаналивает параметр по имени.
- has()
-
Возвращает
true
, если параметр определён. - remove()
- Удаляет параметр.
Экземпляр ParameterBag также имеет некоторые методы для фильтрации значений ввода:
- getAlpha()
- Возвращает алфавитные символы значения параметра;
- getAlnum()
- Возвращает алфавитные символы и цифры значения параметра;
- getBoolean()
- Возвращает значение параметра преобразованное в булево значение;
- getDigits()
- Возвращает цифры значения параметра;
- getInt()
- Возвращает значение параметра, преобразованное в целое число;
- getEnum()
- Возвращает значение параметра, преобразованное в PHP-исчисление;
- getString()
- Возвращает значение параметра в виде строки;
- filter()
-
Фильтрует параметр, используя функцию filter_var.
Если будут найдены невалидные значения, будет вызвано
BadRequestHttpException.
Флажок
FILTER_NULL_ON_FAILURE
может быть использован для игнорирования недопустимых значений.
Все геттеры имеют до двух аргументов: первый - это имя параметра, а второй - значение по умолчанию, которое нужно вернуть, если параметр не существует:
1 2 3 4 5 6 7 8 9 10
// строка запроса - '?foo=bar'
$request->query->get('foo');
// возвращает 'bar'
$request->query->get('bar');
// возвращает null
$request->query->get('bar', 'baz');
// возвращает 'baz'
Когда PHP импортирует запрос на запрос, он обрабатывает параметры запроса
как foo[bar]=baz
, особенным способом, создавая массив. Метод get()
не поддерживает возвращение массивов, поэтому вам нужно использовать следующий
код:
1 2 3 4 5 6 7 8 9 10 11
// строка запроса - '?foo[bar]=baz'
// не используйте $request->query->get('foo'); вместо этого, используйте следующее:
$request->query->all()['foo'];
// возвращает ['bar' => 'baz']
$request->query->get('foo[bar]');
// возвращает null
$request->query->get('foo')['bar'];
// возвращает 'baz'
Благодаря публичному свойству attributes
, вы можете хранить дополнительные
данные в запросе, который также является экземпляром ParameterBag.
Это в основном используется для присоединения информации, которая принадлежит
Запросу и должна быть доступна из множества точек вашего приложения.
Наконец, сырые данные, отправленные в теле запроса, могут быть доступны, используя getContent():
1
$content = $request->getContent();
Например, это может быть полезно для обработки XML-строки, отправленной приложению удалённым сервисом, использующим метод HTTP POST.
Если тело запроса - это JSON-строка, доступ к нему можно получить используя toArray():
1
$data = $request->toArray();
Если данные запроса могут быть данными $_POST
или строкой JSON, то можно использовать
метод getPayload(), который возвращает
экземпляр InputBag, оборачивающий эти данные:
1
$data = $request->getPayload();
Идентификация запроса
В вашем приложении,вам нужен способ идентифицировать запрос; в большинстве случаев, это делается через "путь информации" запроса, который доступен через метод getPathInfo():
1 2 3
// для запроса к http://example.com/blog/index.php/post/hello-world
// путь информации - "/post/hello-world"
$request->getPathInfo();
Симуляция запроса
Вместо создания запроса, основанного на глобальных PHP, вы также можете симулировать запрос:
1 2 3 4 5
$request = Request::create(
'/hello-world',
'GET',
array('name' => 'Fabien')
);
Метод create() создаёт запрос, основанный на URI, методе и некоторых параметрах (параметры запроса или запроса (query), в зависимости от HTTP метода); и конечно, вы также можете переопределить все другие переменные (по умолчанию, Symfony создаёт разумные значения по умолчанию для всех глобальных переменных PHP).
Основываясь на таком запросе, вы можете переопределить глобальные переменные PHP через overrideGlobals():
1
$request->overrideGlobals();
Tip
Вы также можете дублировать существующий запрос через duplicate() или изменить кучу параметров единственным вызовом к initialize().
Доступ к сессии
Если у вас есть сессия, присоединённая к запросу, вы можете получить к ней
доступ через метод getSession()
классов Request
или RequestStack;
hasPreviousSession()
сообщает вам, содержит ли запрос сессию, которая была запущена в одном из
предыдущих запросов.
Обработка заголовков HTTP
Обработка заголовков HTTP - это нетривиальная задача из-за экранирования и обработки пробельных символов внитри заголовков. Symfony предоставляет класс HeaderUtils, который позволяет абстрагировать эту сложность и определяет несколько методов для наиболее частых задач:
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
use Symfony\Component\HttpFoundation\HeaderUtils;
// Разбивает заголовок HTTP с помощью одного или нескольких разделителей
HeaderUtils::split('da, en-gb;q=0.8', ',;')
// => [['da'], ['en-gb','q=0.8']]
// Объединяет массив массивов в один ассоциативный массив
HeaderUtils::combine(array(array('foo', 'abc'), array('bar')))
// => array('foo' => 'abc', 'bar' => true)
// Объединяет ассоциативный массив в строку для использования в заголовке HTTP
HeaderUtils::toString(['foo' => 'abc', 'bar' => true, 'baz' => 'a b c'], ',');
// => 'foo=abc, bar, baz="a b c"'
// Экранирует строку, если необходимо
HeaderUtils::quote('foo "bar"')
// => 'foo \"bar\"'
// Деэкранирует строку
HeaderUtils::unquote('foo \"bar\"')
// => 'foo "bar"'
// Аналищирует строку запросу, но оставляет точки (PHP parse_str() заменяет '.' на '_')
HeaderUtils::parseQuery('foo[bar.baz]=qux');
// => ['foo' => ['bar.baz' => 'qux']]
Доступ к данным заголовков Accept-*
Вы можете с лёгкостью получить доступ к базовым данным, извлечённым из
заголовков Accept-*
, используя следующие методы:
- getAcceptableContentTypes()
- Возвращает список приемлемых типов содержания, в порядке снижения качества.
- getLanguages()
- Возвращает список приемлемых языков, в порядке снижения качества.
- getCharsets()
- Возвращает список приемлемых наборов символов, в порядке снижения качества.
- getEncodings()
- Возвращает список приемлемых кодировок, в порядке снижения качества.
Если вам нужно получить полный доступ к проанализированным данным из Accept
,
Accept-Language
, Accept-Charset
или Accept-Encoding
, вы можете использовать
класс утилиты AcceptHeader:
1 2 3 4 5 6 7 8 9 10 11 12
use Symfony\Component\HttpFoundation\AcceptHeader;
$acceptHeader = AcceptHeader::fromString($request->headers->get('Accept'));
if ($acceptHeader->has('text/html')) {
$item = $acceptHeader->get('text/html');
$charset = $item->getAttribute('charset', 'utf-8');
$quality = $item->getQuality();
}
// Приемлемые объекты заголовков сортируются в порядке снижения качества
$acceptHeaders = AcceptHeader::fromString($request->headers->get('Accept'))
->all();
Также поддерживаются значения по умолчанию, которые могут быть опционально включены
в заголовки Accept-*
:
1 2 3 4 5
$acceptHeader = 'text/plain;q=0.5, text/html, text/*;q=0.8, */*;q=0.3';
$accept = AcceptHeader::fromString($acceptHeader);
$quality = $accept->get('text/xml')->getQuality(); // $quality = 0.8
$quality = $accept->get('application/xml')->getQuality(); // $quality = 0.3
Анонимизация IP-адресов
Все чаще возникает потребность приложений соответствовать регуляциям защити
пользователей - анонимизации IP-адресов до логирования и их сохранения в целях
анализа. Используйте метод anonymize()
из
IpUtils чтобы сделать это:
1 2 3 4 5 6 7 8 9
use Symfony\Component\HttpFoundation\IpUtils;
$ipv4 = '123.234.235.236';
$anonymousIpv4 = IpUtils::anonymize($ipv4);
// $anonymousIpv4 = '123.234.235.0'
$ipv6 = '2a01:198:603:10:396e:4789:8e99:890f';
$anonymousIpv6 = IpUtils::anonymize($ipv6);
// $anonymousIpv6 = '2a01:198:603:10::'
Проверка принадлежности IP к приватной подсети
Если необходимо узнать, принадлежит ли IP-адрес к приватной подсети, можно
использовать метод isPrivateIp()
из пакета
IpUtils для этого:
1 2 3 4 5 6 7 8 9
use Symfony\Component\HttpFoundation\IpUtils;
$ipv4 = '192.168.1.1';
$isPrivate = IpUtils::isPrivateIp($ipv4);
// $isPrivate = true
$ipv6 = '2a01:198:603:10:396e:4789:8e99:890f';
$isPrivate = IpUtils::isPrivateIp($ipv6);
// $isPrivate = false
Сопоставление запроса с набором правил
Компонент HttpFoundation предоставляет несколько классов-сопоставителей, которые позволяют вам
проверить, соответствует ли данный запрос определенным условиям (например, исходит ли он с некоторого IP
адреса, использует ли определенный метод HTTP и т. д.):
- AttributesRequestMatcher
- ExpressionRequestMatcher
- HeaderRequestMatcher
- HostRequestMatcher
- IpsRequestMatcher
- IsJsonRequestMatcher
- MethodRequestMatcher
- PathRequestMatcher
- PortRequestMatcher
- QueryParameterRequestMatcher
- SchemeRequestMatcher
Вы можете использовать их по отдельности или комбинировать, используя ChainRequestMatcher class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
use Symfony\Component\HttpFoundation\ChainRequestMatcher;
use Symfony\Component\HttpFoundation\RequestMatcher\HostRequestMatcher;
use Symfony\Component\HttpFoundation\RequestMatcher\PathRequestMatcher;
use Symfony\Component\HttpFoundation\RequestMatcher\SchemeRequestMatcher;
// использовать только один критерий для сопоставления с запросом
$schemeMatcher = new SchemeRequestMatcher('https');
if ($schemeMatcher->matches($request)) {
// ...
}
// использовать набор критериев для сопоставления с запросом
$matcher = new ChainRequestMatcher([
new HostRequestMatcher('example.com'),
new PathRequestMatcher('/admin'),
]);
if ($matcher->matches($request)) {
// ...
}
7.1
HeaderRequestMatcher
и QueryParameterRequestMatcher
были представлены в Symfony 7.1.
Доступ к другим данным
Класс Request
имеет множество других методов, которые вы можете
использовать для доступа к информации запроса. Посмотрите на
API Запроса,
чтобы узнать больше о них.
Переопределение запроса
Класс Request
не должен быть переопределён, так как это объект данных,
который представляет HTTP сообщение. Но при перемещении из системы наследования,
добавление методов или изменение некоторого поведения по умолчанию может
помочь. В этом случае, зарегистрируйте вызываемое PHP, которое может создать
экземпляр вашего класса Request
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
use App\Http\SpecialRequest;
use Symfony\Component\HttpFoundation\Request;
Request::setFactory(function (
array $query = [],
array $request = [],
array $attributes = [],
array $cookies = [],
array $files = [],
array $server = [],
$content = null
) {
return new SpecialRequest(
$query,
$request,
$attributes,
$cookies,
$files,
$server,
$content
);
});
$request = Request::createFromGlobals();
Ответ
Объект Response содержит всю информацию, которую нужно отпрвить обратно клиенту из заданного запроса. Конструктор имеет до трёх аргументов: содержимое ответа, статус-код и массив HTTP-заголовков:
1 2 3 4 5 6 7
use Symfony\Component\HttpFoundation\Response;
$response = new Response(
'Content',
Response::HTTP_OK,
['content-type' => 'text/html']
);
Эту информацию можно также изменять после создания объекта Ответ:
1 2 3 4 5 6
$response->setContent('Hello World');
// публичный атрибут заголовка - ResponseHeaderBag
$response->headers->set('Content-Type', 'text/plain');
$response->setStatusCode(Response::HTTP_NOT_FOUND);
При установке Content-Type
Ответа, вы можете установить набор символов,
но лучше устанавливать его через метод
setCharset():
1
$response->setCharset('ISO-8859-1');
Отметьте, что по умолчанию, Symfony предполагает, что ваши Ответы зашифрованы с помощью UTF-8.
Отправка ответа
До отправки Ответа вы можете по опционально вызвать метод
prepare(), чтобы исправить
любую несовместимость со спецификацией HTTP (например, неправильный заголовок
Content-Type
):
1
$response->prepare($request);
Отправка ответа клиенту в таком случае заключается в простом вызове send():
1
$response->send();
Метод send()` принимает необязательный аргумент
flush. Если он установлен в значение
false, то такие функции, как
fastcgi_finish_request() или
litespeed_finish_request()`` не вызываются. Это полезно при отладке
вашего приложения, чтобы увидеть, какие исключения вызываются в слушателях
TerminateEvent. Вы можете узнать
больше об этом в
в специальном разделе о событиях ядра .
Установка куки
Cookie ответа могут быть изменены через публичный атрибут headers
:
1 2 3
use Symfony\Component\HttpFoundation\Cookie;
$response->headers->setCookie(Cookie::create('foo', 'bar'));
Метод setCookie() берёт экземпляр Cookie в качестве аргумента.
Вы можете очистить cookie методом clearCookie().
В дополнение к методу Cookie::create()
, вы можете создать объект Cookie
из исходного значения заголовка используя метод fromString().
Вы также можете использовать методы with*()
, чтобы изменить какое-то свойство Cookie
(или создать весь Cookie используя свободный интерфейс). Каждый метод with*()
возвращает
новый объект с модифицированным свойством:
1 2 3 4 5
$cookie = Cookie::create('foo')
->withValue('bar')
->withExpires(strtotime('Fri, 20-May-2011 15:25:52 GMT'))
->withDomain('.example.com')
->withSecure(true);
Можно определить разделенные куки, также известные как CHIPS, с помощью метода withPartitioned():
1 2 3 4 5 6
$cookie = Cookie::create('foo')
->withValue('bar')
->withPartitioned();
// вы также можете установить разделенный аргумент как true при использовании метода фабрики `create()`
$cookie = Cookie::create('name', 'value', partitioned: true);
Управление HTTP-кешем
Класс Response имеет богатый набор методов для управления HTTP-заголовками, относящимися к кешу:
- setPublic()
- setPrivate()
- expire()
- setExpires()
- setMaxAge()
- setSharedMaxAge()
- setStaleIfError()
- setStaleWhileRevalidate()
- setTtl()
- setClientTtl()
- setLastModified()
- setEtag()
- setVary()
Note
Методы setExpires(),
setLastModified() и
setDate() рринимают
любой объект, релизующий \DateTimeInterface
, включая неизменные объекты
дат.
Метод setCache() может быть использован дляустановки наиболее используемой кещ-информации в одном вызове метода:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
$response->setCache([
'must_revalidate' => false,
'no_cache' => false,
'no_store' => false,
'no_transform' => false,
'public' => true,
'private' => false,
'proxy_revalidate' => false,
'max_age' => 600,
's_maxage' => 600,
'stale_if_error' => 86400,
'stale_while_revalidate' => 60,
'immutable' => true,
'last_modified' => new \DateTime(),
'etag' => 'abcdef',
]);
Чтобы проверить, соответствуют ли валидаторы Ответа (ETag
, Last-Modified
)
условному значению, указанному в Запросе клиента, используйте метод
isNotModified():
1 2 3
if ($response->isNotModified($request)) {
$response->send();
}
Если Ответ не был изменён, он устанавливает статус-код 304 и удаляет настоящее содержимое ответа.
Перенаправление пользователя
Чтобы перенаправить клиента по другому URL, вы можете использовать класс RedirectResponse:
1 2 3
use Symfony\Component\HttpFoundation\RedirectResponse;
$response = new RedirectResponse('http://example.com/');
Потоковая передача ответа
Класс StreamedResponse позволяет вам создавать поток с Ответом для клиента. Содержимое ответа представляется PHP вызываемым, а не строкой:
1 2 3 4 5 6 7 8 9 10 11
use Symfony\Component\HttpFoundation\StreamedResponse;
$response = new StreamedResponse();
$response->setCallback(function (): void {
var_dump('Hello World');
flush();
sleep(2);
var_dump('Hello World');
flush();
});
$response->send();
Note
Функция flush()
не сбрасывает буферизацию. Если ob_start()
был вызван
до этого, или включена опция output_buffering
php.ini
, то вы должны
вызывать ob_flush()
до flush()
.
Кроме того, PHP не единственный слой, буферизирующий вывод. Ваш веб-сервер может также использовать буфер, в зависимости от конфигурации. Некоторые серверы, вроде Nginx, позволяют вам отключать буферизацию на уровне конфигурации или добавлять спеицальный заголовок HTTP в ответе:
1 2
// отключает буферизацию FastCGI в Nginx только для этого ответа
$response->headers->set('X-Accel-Buffering', 'no');
Потоковая передача JSON-ответа
StreamedJsonResponse позволяет потоковую передачу больших JSON-ответов с помощью PHP-генераторов, что позволяет снизить потребление ресурсов.
Конструктор класса ожидает массив, представляющий собой структуру JSON и включает в себя список содержания для потоковой передачи. В дополнение к PHP-генераторам, которые рекомендуются для минимизации использования памяти, он также поддерживает любой тип PHP Traversable, содержащий JSON-сериализуемые данные:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
use Symfony\Component\HttpFoundation\StreamedJsonResponse;
// любой метод или функция, которые возвращают Генератор PHP
function loadArticles(): \Generator {
yield ['title' => 'Article 1'];
yield ['title' => 'Article 2'];
yield ['title' => 'Article 3'];
};
$response = new StreamedJsonResponse(
// JSON-структура с генераторами, в которых будет потоковая передача в виде списка
[
'_embedded' => [
'articles' => loadArticles(),
],
],
);
При загрузке данных через Doctrine вы можете использовать метод toIterable()
для получения результатов построчно и минимизации потребления ресурсов.
Подробнее см. документацию `пакетная обработка Doctrine`:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
public function __invoke(): Response
{
return new StreamedJsonResponse(
[
'_embedded' => [
'articles' => $this->loadArticles(),
],
],
);
}
public function loadArticles(): \Generator
{
// как-то получить $entityManager (например, через внедрение конструктора)
$entityManager = ...
$queryBuilder = $entityManager->createQueryBuilder();
$queryBuilder->from(Article::class, 'article');
$queryBuilder->select('article.id')
->addSelect('article.title')
->addSelect('article.description');
return $queryBuilder->getQuery()->toIterable();
}
Если вы возвращаете много данных, рассмотрите возможность вызова функции flush после некоторого определенного количества элементов, чтобы отправить содержание в браузер:
1 2 3 4 5 6 7 8 9 10 11 12 13
public function loadArticles(): \Generator
{
// ...
$count = 0;
foreach ($queryBuilder->getQuery()->toIterable() as $article) {
yield $article;
if (0 === ++$count % 100) {
flush();
}
}
}
Кроме того, вы можете передать любое итерируемое значение в StreamedJsonResponse
,
включая генераторы:
public function loadArticles(): Generator { yield ['title' => 'Article 1']; yield ['title' => 'Article 2']; yield ['title' => 'Article 3']; }
public function __invoke(): Response { // ...
return new StreamedJsonResponse(loadArticles());
}
Подача файлов
При отправке файла вы должны добавлять заголовок Content-Disposition
к
вашему ответу. И хотя создание этого заголовка для базовых загрузок файлов
- это просто, использование не ASCII имён файлов требует больших усилий.
makeDisposition()
абстрагирует тяжелую работу, скрывающуюся за простым API:
1 2 3 4 5 6 7 8 9 10 11 12 13
use Symfony\Component\HttpFoundation\HeaderUtils;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
$fileContent = ...; // the generated file content
$response = new Response($fileContent);
$disposition = HeaderUtils::makeDisposition(
HeaderUtils::DISPOSITION_ATTACHMENT,
'foo.pdf'
);
$response->headers->set('Content-Disposition', $disposition);
Как вариант, если вы подаёте статичный файл, вы можете использовать BinaryFileResponse:
1 2 3 4
use Symfony\Component\HttpFoundation\BinaryFileResponse;
$file = 'path/to/file.txt';
$response = new BinaryFileResponse($file);
BinaryFileResponse
автоматически обработает заголовки Range
и
If-Range
из запроса. Он также поддерживаетX-Sendfile
(см. Nginx
и Apache). Чтобы воспользоваться этим, вам нужно определить, стоит ли
доверять заголовку X-Sendfile-Type
и вызвать
trustXSendfileTypeHeader(),
если стоит:
1
BinaryFileResponse::trustXSendfileTypeHeader();
Note
BinaryFileResponse
будет обрабатывать X-Sendfile
только если присутствует определенный заголовок.
Для Apache, это по умолчанию не так.
Чтобы добавить заголовок, используйте модуль Apache mod_headers
и добавьте следующее в конфигурацию Apache:
1 2 3 4 5 6 7 8 9 10
<IfModule mod_xsendfile.c>
# This is already present somewhere...
XSendFile on
XSendFilePath ...some path...
# This needs to be added:
<IfModule mod_headers.c>
RequestHeader set X-Sendfile-Type X-Sendfile
</IfModule>
</IfModule>
С BinaryFileResponse
вы можете продолжать устанавливать Content-Type
отправленного файла, или изменять его Content-Disposition
:
1 2 3 4 5 6
// ...
$response->headers->set('Content-Type', 'text/plain');
$response->setContentDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
'filename.txt'
);
Файл можно удалить после отправки запроса методом
deleteFileAfterSend().
Пожалуйста, заметьте, что это не работает, если установлен заголовок X-Sendfile
.
В качестве альтернативы, BinaryFileResponse
поддерживает экземпляры \SplTempFileObject
.
Это полезно, когда вы хотите обслужить файл, который был создан в памяти
и который будет автоматически удален после отправки ответа:
use SymfonyComponentHttpFoundationBinaryFileResponse;
$file = new SplTempFileObject(); $file->fwrite('Hello World'); $file->rewind();
$response = new BinaryFileResponse($file);
7.1
Поддержка для \SplTempFileObject
и BinaryFileResponse
была представлена в Symfony 7.1.
Если размер поданого файла неизвестен (например, потому что он создаётся на лету, или
потому что в нём зарегистрирован фильтр потока PHP и т.д.), то вы можете передать
экземпляр Stream
в BinaryFileResponse
. Это отключит обработку Range
и
Content-Length
, переключившись на механизм передачи данных chunked encoding:
1 2 3 4 5
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\Stream;
$stream = new Stream('path/to/stream');
$response = new BinaryFileResponse($stream);
Note
Если вы только создали файл во время этого же запроса, файл может быть
отправлен без содержания. Это может произойти в связи со статистикой кешированного
файла, которая возвращает ноль в качестве размера файла. Чтобы исправить эту
проблему, вызовите clearstatcache(true, $file)
с путём к бинарному файлу.
Создание JSON-ответа
Любой тип ответа может быть создан через класс Response, путём установки правильного содержания и заголовков. JSON ответ может выглядеть так:
1 2 3 4 5 6 7
use Symfony\Component\HttpFoundation\Response;
$response = new Response();
$response->setContent(json_encode([
'data' => 123,
]));
$response->headers->set('Content-Type', 'application/json');
Также существует полезный класс JsonResponse, который может сделать это ешё проще:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
use Symfony\Component\HttpFoundation\JsonResponse;
// если вы знаете, какие данные отправлять при создании запроса
$response = new JsonResponse(['data' => 123]);
// если вы не знаете, какие данные отправлять при создании запроса
$response = new JsonResponse();
// ...
// cконфигурировать любые пользовательские опции шифрования (если необходимо, должно быть вызвано до "setData()")
//$response->setEncodingOptions(JsonResponse::DEFAULT_ENCODING_OPTIONS | \JSON_PRESERVE_ZERO_FRACTION);
$response->setData(['data' => 123]);
// если данные для отправки уже зашифрованы в JSON
$response = JsonResponse::fromJsonString('{ "data": 123 }');
Класс JsonResponse
устанавливает заголовок Content-Type
в
application/json
и шифрует ваши данные в JSON при необходимости.
Danger
Чтобы избежать XSSI перехвата JSON, вам стоит передать ассоциативный массив
в JsonResponse
в качестве крайнего массива, а не индексированного массива,
чтобы финальный результат был объектом (например, {"object": "not inside an array"}
)
вместо массива (например, [{"object": "inside an array"}]
). Прочтите справочник OWASP,
чтобы узнать больше.
Только методы, отвечающие на запросы GET уязвимы к XSSI 'перехвату JSON'. Методы, отвечающие на запросы POST остаются неуязвимыми.
Обратный вызов JSONP
Есди вы используете JSONP, вы можете установить функцию обратного вызова, в которую должны быть переданы данные:
1
$response->setCallback('handleResponse');
В этом случае, заголовок Content-Type
будет text/javascript
, а содержание
ответа будет выглядеть так:
1
handleResponse({'data': 123});
Сессия
Информация сессии хранится в отдельном документе: Управление сессиями.
Параметр безопасного контента
Некоторые веб-сайты имеют "безопасный" режим, чтобы помочь тем, кто не хочет видеть контент, который может их оскорбить. Спецификация RFC 8674 определяет способ, которым агенты пользователя запрашивают безопасный контент у сервера.
Спецификация не определеяет, какой контент может считаться оскорбительным, поэтому концепция "безопасности" определена не точно. Скорее этот термин интерпретируется сервером и в рамках полномочий каждого сайта решить, как действовать с этой информацией.
Symfony предлагает два метода взаимодействия с этим параметром:
Следующий пример показывает, как определить, что агент пользователя предпочитает "безопасный" контент:
1 2 3 4 5 6 7
if ($request->preferSafeContent()) {
$response = new Response($alternativeContent);
// сообщает пользователю, что мы учли его предпочтения
$response->setContentSafe();
return $response;
}
Генерирование относительных и абсолютных URL
Генерирование абсолютных и относительных URL для заданного пути - распространенная необходимость в некоторых приложениях. В шаблонах Twig вы можете использовать функции absolute_url() и relative_path() , чтобы сделать это.
Класс UrlHelper предоставляет те же функции
для PHP-кода через методы getAbsoluteUrl()
и getRelativePath()
. Вы можете внедрить
это в качестве сервиса где угодно в вашем приложении:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// src/Normalizer/UserApiNormalizer.php
namespace App\Normalizer;
use Symfony\Component\HttpFoundation\UrlHelper;
class UserApiNormalizer
{
public function __construct(
private UrlHelper $urlHelper,
) {
}
public function normalize($user): array
{
return [
'avatar' => $this->urlHelper->getAbsoluteUrl($user->avatar()->path()),
];
}
}
Узнайте больше
- Контроллер
- Расширения разрешения аргумента действия
- Как настроить страницы ошибок
- Как пересылать запросы другому контроллеру
- Как определять контроллеры как сервисы
- Как создать SOAP веб-сервис в контроллере Symfony
- Как загружать файлы
- Расширение разрещения аргумента действия
- Сессии
- Инвалидация кеша
- Варьирование ответа для HTTP-кеша
- Работа с включениями крайней стороны
- Срок действия HTTP-кеша
- Кеширование страниц, содержащих CSRF-защищённые формы
- Работа с включениями серверной стороны (SSI)
- Валидация HTTP-кеша
- Как использовать Varnish для ускорения моего сайта