|
|
|
Ответы
|
|
|
|
======
|
|
|
|
|
|
|
|
Когда приложение заканчивает обработку [запроса](runtime-requests.md), оно генерирует объект [[yii\web\Response|ответа]]
|
|
|
|
и отправляет его пользователю. Объект ответа содержит такие данные, как HTTP-код состояния, HTTP-заголовки и тело ответа.
|
|
|
|
Конечная цель разработки Web-приложения состоит в создании объектов ответа на различные запросы.
|
|
|
|
|
|
|
|
В большинстве случаев вам придется иметь дело с [компонентом приложения](structure-application-components.md) `response`,
|
|
|
|
который по умолчанию является экземпляром класса [[yii\web\Response]]. Однако Yii также позволяет вам создавать собственные
|
|
|
|
объекты ответа и отправлять их пользователям. Это будет рассмотрено ниже.
|
|
|
|
|
|
|
|
В данном разделе мы опишем, как составлять ответы и отправлять их пользователям.
|
|
|
|
|
|
|
|
|
|
|
|
## Код состояния <a name="status-code"></a>
|
|
|
|
|
|
|
|
Первое, что вы делаете при построении ответа, — определяете, был ли успешно обработан запрос. Это реализуется заданием
|
|
|
|
свойству [[yii\web\Response::statusCode]] значения, которое может быть одним из валидных
|
|
|
|
[HTTP-кодов состояния](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html). Например, чтобы показать, что запрос был
|
|
|
|
успешно обработан, вы можете установить значение кода состояния равным 200:
|
|
|
|
|
|
|
|
```php
|
|
|
|
Yii::$app->response->statusCode = 200;
|
|
|
|
```
|
|
|
|
|
|
|
|
Однако в большинстве случаев явная установка не требуется так как значение [[yii\web\Response::statusCode]]
|
|
|
|
по умолчанию равно 200. Если же вам нужно показать, что запрос не удался, вы можете выбросить соответствующее
|
|
|
|
HTTP-исключение:
|
|
|
|
|
|
|
|
```php
|
|
|
|
throw new \yii\web\NotFoundHttpException;
|
|
|
|
```
|
|
|
|
|
|
|
|
Когда [обработчик ошибок](runtime-handling-errors.md) поймает исключение, он извлечёт код состояния
|
|
|
|
из исключения и назначит его ответу. Исключение [[yii\web\NotFoundHttpException]] в коде выше
|
|
|
|
представляет HTTP-код состояния 404. В Yii предопределены следующие HTTP-исключения:
|
|
|
|
|
|
|
|
* [[yii\web\BadRequestHttpException]]: код состояния 400.
|
|
|
|
* [[yii\web\ConflictHttpException]]: код состояния 409.
|
|
|
|
* [[yii\web\ForbiddenHttpException]]: код состояния 403.
|
|
|
|
* [[yii\web\GoneHttpException]]: код состояния 410.
|
|
|
|
* [[yii\web\MethodNotAllowedHttpException]]: код состояния 405.
|
|
|
|
* [[yii\web\NotAcceptableHttpException]]: код состояния 406.
|
|
|
|
* [[yii\web\NotFoundHttpException]]: код состояния 404.
|
|
|
|
* [[yii\web\ServerErrorHttpException]]: код состояния 500.
|
|
|
|
* [[yii\web\TooManyRequestsHttpException]]: код состояния 429.
|
|
|
|
* [[yii\web\UnauthorizedHttpException]]: код состояния 401.
|
|
|
|
* [[yii\web\UnsupportedMediaTypeHttpException]]: код состояния 415.
|
|
|
|
|
|
|
|
Если в приведённом выше списке нет исключения, которое вы хотите выбросить, вы можете создать его, расширив класс
|
|
|
|
[[yii\web\HttpException]], или выбросить его напрямую с кодом состояния, например:
|
|
|
|
|
|
|
|
```php
|
|
|
|
throw new \yii\web\HttpException(402);
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## HTTP-заголовки <a name="http-headers"></a>
|
|
|
|
|
|
|
|
Вы можете отправлять HTTP-заголовки, работая с [[yii\web\Response::headers|коллекцией заголовков]] компонента `response`:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$headers = Yii::$app->response->headers;
|
|
|
|
|
|
|
|
// добавить заголовок Pragma. Уже имеющиеся Pragma-заголовки НЕ будут перезаписаны.
|
|
|
|
$headers->add('Pragma', 'no-cache');
|
|
|
|
|
|
|
|
// установить заголовок Pragma. Любые уже имеющиеся Pragma-заголовки будут сброшены.
|
|
|
|
$headers->set('Pragma', 'no-cache');
|
|
|
|
|
|
|
|
// удалить заголовок (или заголовки) Pragma и вернуть их значения массивом
|
|
|
|
$values = $headers->remove('Pragma');
|
|
|
|
```
|
|
|
|
|
|
|
|
> Информация: названия заголовков не чувствительны к регистру символов. Заново зарегистрированные заголовки не отсылаются
|
|
|
|
пользователю до вызова [[yii\web\Response::send()]].
|
|
|
|
|
|
|
|
|
|
|
|
## Тело ответа <a name="response-body"></a>
|
|
|
|
|
|
|
|
Большинство ответов должны иметь тело, содержащее то, что вы хотите показать пользователям.
|
|
|
|
|
|
|
|
Если у вас уже имеется отформатированная строка для тела, вы можете присвоить её свойству [[yii\web\Response::content]]
|
|
|
|
объекта запроса:
|
|
|
|
|
|
|
|
```php
|
|
|
|
Yii::$app->response->content = 'hello world!';
|
|
|
|
```
|
|
|
|
|
|
|
|
Если ваши данные перед отправкой конечным пользователям нужно привести к определённому формату, вам следует установить значения
|
|
|
|
двух свойств: [[yii\web\Response::format|format]] и [[yii\web\Response::data|data]]. Свойство [[yii\web\Response::format|format]]
|
|
|
|
определяет, в каком формате следует возвращать данные из [[yii\web\Response::data|data]]. Например:
|
|
|
|
|
|
|
|
```php
|
|
|
|
$response = Yii::$app->response;
|
|
|
|
$response->format = \yii\web\Response::FORMAT_JSON;
|
|
|
|
$response->data = ['message' => 'hello world'];
|
|
|
|
```
|
|
|
|
|
|
|
|
Yii из коробки имеет поддержку следующих форматов, каждый из которых реализован классом [[yii\web\ResponseFormatterInterface|форматтера]].
|
|
|
|
Вы можете настроить эти форматтеры или добавить новые через свойство [[yii\web\Response::formatters]].
|
|
|
|
|
|
|
|
* [[yii\web\Response::FORMAT_HTML|HTML]]: реализуется классом [[yii\web\HtmlResponseFormatter]].
|
|
|
|
* [[yii\web\Response::FORMAT_XML|XML]]: реализуется классом [[yii\web\XmlResponseFormatter]].
|
|
|
|
* [[yii\web\Response::FORMAT_JSON|JSON]]: реализуется классом [[yii\web\JsonResponseFormatter]].
|
|
|
|
* [[yii\web\Response::FORMAT_JSONP|JSONP]]: реализуется классом [[yii\web\JsonResponseFormatter]].
|
|
|
|
|
|
|
|
Хотя тело запроса может быть явно установлено показанным выше способом, в большинстве случаев вы можете задавать его неявно
|
|
|
|
через возвращаемое значение методов [действий](structure-controllers.md). Типичный пример использования:
|
|
|
|
|
|
|
|
```php
|
|
|
|
public function actionIndex()
|
|
|
|
{
|
|
|
|
return $this->render('index');
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Действие `index` в коде выше возвращает результат рендеринга представления `index`. Возвращаемое значение будет взято
|
|
|
|
компонентом `response`, отформатировано и затем отправлено пользователям.
|
|
|
|
|
|
|
|
Так как по умолчанию форматом ответа является [[yii\web\Response::FORMAT_HTML|HTML]], в методе действия следует
|
|
|
|
вернуть строку. Если вы хотите использовать другой формат ответа, необходимо настроить его перед отправкой данных:
|
|
|
|
|
|
|
|
```php
|
|
|
|
public function actionInfo()
|
|
|
|
{
|
|
|
|
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
|
|
|
|
return [
|
|
|
|
'message' => 'hello world',
|
|
|
|
'code' => 100,
|
|
|
|
];
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Как уже было сказано, кроме использования стандартного компонента приложения `response` вы также можете создавать свои
|
|
|
|
объекты ответа и отправлять их конечным пользователям. Вы можете сделать это, возвращая такой объект в методе действия:
|
|
|
|
|
|
|
|
```php
|
|
|
|
public function actionInfo()
|
|
|
|
{
|
|
|
|
return \Yii::createObject([
|
|
|
|
'class' => 'yii\web\Response',
|
|
|
|
'format' => \yii\web\Response::FORMAT_JSON,
|
|
|
|
'data' => [
|
|
|
|
'message' => 'hello world',
|
|
|
|
'code' => 100,
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
> Примечание: создавая собственные объекты ответов, вы не сможете воспользоваться конфигурацией компонента `response`,
|
|
|
|
настроенной вами в конфигурации приложения. Тем не менее, вы можете воспользоваться
|
|
|
|
[внедрением зависимости](concept-di-container.md), чтобы применить общую конфигурацию к вашим новым объектам ответа.
|
|
|
|
|
|
|
|
|
|
|
|
## Перенаправление браузера <a name="browser-redirection"></a>
|
|
|
|
|
|
|
|
Перенаправление браузера основано на отправке HTTP-заголовка `Location`. Так как данная возможность широко применяется,
|
|
|
|
Yii имеет средства для её использования.
|
|
|
|
|
|
|
|
Вы можете перенаправить браузер пользователя на URL-адрес, вызвав метод [[yii\web\Response::redirect()]]. Этот метод
|
|
|
|
использует указанный URL-адрес в качестве значения заголовка `Location` и возвращает сам объект ответа. В методе действия
|
|
|
|
вы можете вызвать короткую версию этого метода — [[yii\web\Controller::redirect()]]. Например:
|
|
|
|
|
|
|
|
```php
|
|
|
|
public function actionOld()
|
|
|
|
{
|
|
|
|
return $this->redirect('http://example.com/new', 301);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
В приведённом выше коде метод действия возвращает результат `redirect()`. Как говорилось выше, объект ответа,
|
|
|
|
возвращаемый методом действия, будет использоваться в качестве ответа конечным пользователям.
|
|
|
|
|
|
|
|
В коде, находящемся вне методов действий, следует использовать [[yii\web\Response::redirect()]] и непосредственно после
|
|
|
|
него — метод [[yii\web\Response::send()]]. Так можно быть уверенным, что к ответу не будет добавлено нежелательное
|
|
|
|
содержимое.
|
|
|
|
|
|
|
|
```php
|
|
|
|
\Yii::$app->response->redirect('http://example.com/new', 301)->send();
|
|
|
|
```
|
|
|
|
|
|
|
|
> Информация: По умолчанию метод [[yii\web\Response::redirect()]] устанавливает код состояния ответа равным 302, сообщая
|
|
|
|
браузеру, что запрашиваемый ресурс *временно* находится по другому URI-адресу. Вы можете передать код состояния
|
|
|
|
301, чтобы сообщить браузеру, что ресурс перемещён *навсегда*.
|
|
|
|
|
|
|
|
Если текущий запрос является AJAX-запросом, отправка заголовка `Location` не заставит браузер автоматически
|
|
|
|
осуществить перенаправление. Чтобы решить эту задачу, метод [[yii\web\Response::redirect()]] устанавливает значение
|
|
|
|
заголовка `X-Redirect` равным URL для перенаправления. На стороне клиента вы можете написать JavaScript-код для чтения
|
|
|
|
значения этого заголовка и перенаправления браузера соответственно.
|
|
|
|
|
|
|
|
> Информация: Yii поставляется с JavaScript-файлом `yii.js`, который предоставляет набор часто используемых
|
|
|
|
JavaScript-утилит, включая и перенаправление браузера на основе заголовка `X-Redirect`. Следовательно, если вы
|
|
|
|
используете этот JavaScript-файл (зарегистрировав пакет ресурсов [[yii\web\YiiAsset]]), вам не нужно писать
|
|
|
|
дополнительный код для поддержки AJAX-перенаправления.
|
|
|
|
|
|
|
|
|
|
|
|
## Отправка файлов <a name="sending-files"></a>
|
|
|
|
|
|
|
|
Как и перенаправление браузера, отправка файлов является ещё одной возможностью, основанной на определённых HTTP-заголовках.
|
|
|
|
Yii предоставляет набор методов для решения различных задач по отправке файлов. Все они поддерживают HTTP-заголовок range.
|
|
|
|
|
|
|
|
* [[yii\web\Response::sendFile()]]: отправляет клиенту существующий файл.
|
|
|
|
* [[yii\web\Response::sendContentAsFile()]]: отправляет клиенту строку как файл.
|
|
|
|
* [[yii\web\Response::sendStreamAsFile()]]: отправляет клиенту существующий файловый поток как файл.
|
|
|
|
|
|
|
|
Эти методы имеют одинаковую сигнатуру и возвращают объект ответа. Если отправляемый файл очень велик, следует
|
|
|
|
использовать [[yii\web\Response::sendStreamAsFile()]], так как он более эффективно использует оперативную память.
|
|
|
|
Следующий пример показывает, как отправить файл в действии контроллера:
|
|
|
|
|
|
|
|
```php
|
|
|
|
public function actionDownload()
|
|
|
|
{
|
|
|
|
return \Yii::$app->response->sendFile('path/to/file.txt');
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
При вызове метода отправки файла вне методов действий чтобы быть уверенным, что к ответу не будет добавлено никакое
|
|
|
|
нежелательное содержимое, следует вызвать сразу после него [[yii\web\Response::send()]].
|
|
|
|
|
|
|
|
```php
|
|
|
|
\Yii::$app->response->sendFile('path/to/file.txt')->send();
|
|
|
|
```
|
|
|
|
|
|
|
|
Некоторые Web-серверы поддерживают особый режим отправки файлов, который называется *X-Sendfile*. Идея в том, чтобы
|
|
|
|
перенаправить запрос файла Web-серверу, который отдаст файл пользователю самостоятельно. В результате Web-приложение
|
|
|
|
может завершиться раньше, пока Web-сервер ещё пересылает файл. Чтобы использовать эту возможность, воспользуйтесь
|
|
|
|
методом [[yii\web\Response::xSendFile()]]. Далее приведены ссылки на то, как включить `X-Sendfile` для популярных
|
|
|
|
Web-серверов:
|
|
|
|
|
|
|
|
- Apache: [X-Sendfile](http://tn123.org/mod_xsendfile)
|
|
|
|
- Lighttpd v1.4: [X-LIGHTTPD-send-file](http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)
|
|
|
|
- Lighttpd v1.5: [X-Sendfile](http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)
|
|
|
|
- Nginx: [X-Accel-Redirect](http://wiki.nginx.org/XSendfile)
|
|
|
|
- Cherokee: [X-Sendfile and X-Accel-Redirect](http://www.cherokee-project.com/doc/other_goodies.html#x-sendfile)
|
|
|
|
|
|
|
|
|
|
|
|
## Отправка ответа <a name="sending-response"></a>
|
|
|
|
|
|
|
|
Содержимое ответа не отправляется пользователю до вызова метода [[yii\web\Response::send()]]. По умолчанию он вызывается
|
|
|
|
автоматически в конце метода [[yii\base\Application::run()]]. Однако, чтобы ответ был отправлен немедленно, вы можете
|
|
|
|
вызвать этот метод явно.
|
|
|
|
|
|
|
|
Для отправки ответа метод [[yii\web\Response::send()]] выполняет следующие шаги:
|
|
|
|
|
|
|
|
1. Инициируется событие [[yii\web\Response::EVENT_BEFORE_SEND]].
|
|
|
|
2. Для форматирования [[yii\web\Response::data|данных ответа]] в [[yii\web\Response::content|содержимое ответа]]
|
|
|
|
вызывается метод [[yii\web\Response::prepare()]] .
|
|
|
|
3. Инициируется событие [[yii\web\Response::EVENT_AFTER_PREPARE]].
|
|
|
|
4. Для отправки зарегистрированных HTTP-заголовков вызывается метод [[yii\web\Response::sendHeaders()]].
|
|
|
|
5. Для отправки тела ответа вызывается метод [[yii\web\Response::sendContent()]].
|
|
|
|
6. Инициируется событие [[yii\web\Response::EVENT_AFTER_SEND]].
|
|
|
|
|
|
|
|
Повторный вызов [[yii\web\Response::send()]] игнорируется. Это означает, что если ответ уже отправлен, то к нему уже
|
|
|
|
ничего не добавить.
|
|
|
|
|
|
|
|
Как видно, метод [[yii\web\Response::send()]] инициирует несколько полезных событий. Реагируя на
|
|
|
|
эти события, можно настраивать или декорировать ответ.
|