Responses

Когда приложение завершает обработку запроса, оно генерирует объект ответа и отправляет его конечному пользователю. Объект ответа содержит такую информацию, как код состояния HTTP, заголовки HTTP и тело. Конечной целью разработки веб-приложений является построение таких объектов ответа по различным запросам.

В большинстве случаев вы должны в основном иметь дело с компонентом приложения response, который является экземпляром yii\web\Response, по умолчанию. Однако Yii также позволяет создавать собственные объекты ответа и отправлять их конечным пользователям, как мы объясним ниже.

Код статуса

Одна из первых вещей, которые вы сделали бы при построении ответа, - это указать, был ли запрос успешно обработан. Это можно сделать, установив свойство yii\web\Response::$statusCode, которое может принимать один из допустимых кодов состояния HTTP. Например, чтобы указать, что запрос успешно обработан, вы можете установить код статуса 200, например:

Yii::$app->response->statusCode = 200;

Однако в большинстве случаев вам не нужно явно указывать код состояния. Это связано с тем, что значение yii\web\Response::$statusCode по умолчанию равно 200. И если вы хотите указать, что запрос не увенчался успехом, вы можете выдать соответствующее исключение HTTP, например:

throw new \yii\web\NotFoundHttpException;

Когда обработчик ошибок перехватывает исключение, он извлекает код состояния из исключения и присваивает его ответу. Для yii\web\NotFoundHttpException выше, оно связано с HTTP-статусом 404. Следующие исключения HTTP предопределены в Yii:

  • yii\web\BadRequestHttpException: status code 400.
  • yii\web\ConflictHttpException: status code 409.
  • yii\web\ForbiddenHttpException: status code 403.
  • yii\web\GoneHttpException: status code 410.
  • yii\web\MethodNotAllowedHttpException: status code 405.
  • yii\web\NotAcceptableHttpException: status code 406.
  • yii\web\NotFoundHttpException: status code 404.
  • yii\web\ServerErrorHttpException: status code 500.
  • yii\web\TooManyRequestsHttpException: status code 429.
  • yii\web\UnauthorizedHttpException: status code 401.
  • yii\web\UnsupportedMediaTypeHttpException: status code 415.

Если исключение, которое вы хотите выбросить, не входит в список выше, вы можете создать его, расширив его с yii\web\HttpException или напрямую передав его с кодом состояния, например:

throw new \yii\web\HttpException(402);

Заголовки HTTP

Вы можете отправлять HTTP-заголовки, манипулируя коллекцией заголовков в компоненте response. Например:

$headers = Yii::$app->response->headers;

// добавьте заголовок Pragma. Существующие заголовки Pragma НЕ будут перезаписаны.
$headers->add('Pragma', 'no-cache');

// установите заголовок Pragma. Любые существующие заголовки Pragma будут отброшены.
$headers->set('Pragma', 'no-cache');

// удалить заголовки Pragma и вернуть удаленные значения заголовка Pragma в массиве
$values = $headers->remove('Pragma');

Тело ответа

В большинстве ответов должен быть текст, который будет показывать контент, который вы хотите показывать конечным пользователям. Если у вас уже есть отформатированная строка тела, вы можете назначить ее свойству yii\web\Response::$content ответа. Например:

Yii::$app->response->content = 'hello world!';

Если ваши данные нужно отформатировать перед отправкой их конечным пользователям, вы должны установить оба формата и свойства данных. Свойство format указывает, в каком формате данные должны быть отформатированы. Например:

$response = Yii::$app->response;
$response->format = \yii\web\Response::FORMAT_JSON;
$response->data = ['message' => 'hello world'];

Yii поддерживает следующие форматы, каждый из которых реализован классом форматирования. Вы можете настроить эти форматировщики или добавить новые, настроив свойство yii\web\Response::$formatters.

  • HTML: осуществляется yii\web\HtmlResponseFormatter.
  • XML: осуществляется yii\web\XmlResponseFormatter.
  • JSON: осуществляется yii\web\JsonResponseFormatter.
  • JSONP: осуществляется yii\web\JsonResponseFormatter.
  • RAW: используйте этот формат, если вы хотите отправить ответ без применения форматирования.

Хотя тело ответа можно задать явно, как показано выше, в большинстве случаев вы можете установить его неявно с помощью возвращаемого значения методов действия. Общий случай использования выглядит следующим образом:

public function actionIndex()
{
    return $this->render('index');
}

Указанное выше действие index возвращает результат рендеринга представления index. Возвращаемое значение будет принято компонентом response, отформатировано, а затем отправлено конечным пользователям.

Поскольку по умолчанию формат ответа - HTML, вы должны только возвращать строку в методе действия. Если вы хотите использовать другой формат ответа, вы должны установить его перед возвратом данных. Например:

public function actionInfo()
{
    \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
    return [
        'message' => 'hello world',
        'code' => 100,
    ];
}

Как уже упоминалось, помимо использования компонента приложения response по умолчанию, вы также можете создавать свои собственные объекты ответа и отправлять их конечным пользователям. Вы можете сделать это, вернув такой объект в метод действия, например:

public function actionInfo()
{
    return \Yii::createObject([
        'class' => 'yii\web\Response',
        'format' => \yii\web\Response::FORMAT_JSON,
        'data' => [
            'message' => 'hello world',
            'code' => 100,
        ],
    ]);
}

Перенаправление браузера

Перенаправление браузера зависит от отправки HTTP-заголовка Location. Поскольку эта функция обычно используется, Yii обеспечивает некоторую особую поддержку для нее.

Вы можете перенаправить браузер пользователя на URL-адрес, вызвав метод yii\web\Response::redirect(). Метод устанавливает соответствующий заголовок Location с заданным URL-адресом и возвращает сам объект ответа. В методе действия вы можете вызвать его ярлык yii\web\Controller::redirect(). Например:

public function actionOld()
{
    return $this->redirect('http://example.com/new', 301);
}

В приведенном выше коде метод действия возвращает результат метода redirect(). Как объяснялось выше, объект ответа, возвращаемый методом действия, будет использоваться как отсылка ответа конечным пользователям.

В местах, отличных от метода действия, вам следует сразу вызвать yii\web\Response::redirect(), после чего следует связанный вызов метода yii\web\Response::send(), чтобы гарантировать, что дополнительный контент не будет добавлен к ответ.

\Yii::$app->response->redirect('http://example.com/new', 301)->send();

Если текущий запрос является запросом AJAX, отправка заголовка местоположения не приведет автоматически к перенаправлению браузера. Чтобы решить эту проблему, метод yii\web\Response::redirect() задает заголовок X-Redirect с URL-адресом переадресации в качестве его значения. На стороне клиента вы можете написать код JavaScript, чтобы прочитать это значение заголовка и соответственно перенаправить браузер.

Отправка файлов

Подобно переадресации браузера, отправка файлов - еще одна функция, которая использует определенные HTTP-заголовки. Yii предоставляет набор методов для поддержки различных потребностей отправки файлов. Все они имеют встроенную поддержку заголовка диапазона HTTP.

  • yii\web\Response::sendFile(): отправляет существующий файл клиенту.
  • yii\web\Response::sendContentAsFile(): отправляет текстовую строку в виде файла клиенту.
  • yii\web\Response::sendStreamAsFile(): отправляет существующий поток файлов в виде файла клиенту.

Эти методы имеют одинаковую подпись метода с объектом ответа в качестве возвращаемого значения. Если файл, который нужно отправить, очень большой, вы должны рассмотреть возможность использования yii\web\Response::sendStreamAsFile(), потому что он более эффективен в отношении памяти. В следующем примере показано, как отправить файл в действие контроллера:

public function actionDownload()
{
    return \Yii::$app->response->sendFile('path/to/file.txt');
}

Если вы вызываете метод отправки файла в других местах, кроме метода действия, вам также следует вызвать метод yii\web\Response::send(), чтобы гарантировать, что дополнительный текст не будет добавлен в ответ.

\Yii::$app->response->sendFile('path/to/file.txt')->send();

На некоторых веб-серверах имеется специальная поддержка отправки файлов, называемая X-Sendfile. Идея заключается в перенаправлении запроса на файл на веб-сервер, который будет непосредственно служить файлу. В результате веб-приложение может завершиться раньше, пока веб-сервер отправляет файл. Чтобы использовать эту функцию, вы можете вызвать yii\web\Response::xSendFile(). В следующем списке показано, как включить функцию X-Sendfile для некоторых популярных веб-серверов:

  • Apache: X-Sendfile
  • Lighttpd v1.4: X-LIGHTTPD-send-file
  • Lighttpd v1.5: X-Sendfile
  • Nginx: X-Accel-Redirect
  • Cherokee: X-Sendfile and X-Accel-Redirect

Отправка ответа

Содержимое в ответе не отправляется пользователю до тех пор, пока не будет вызван метод yii\web\Response::send(). По умолчанию этот метод будет вызываться автоматически в конце yii\base\Application::run(). Однако вы можете явно вызвать этот метод, чтобы немедленно отправить ответ.

Метод yii\web\Response::send() выполняет следующие шаги для отправки ответа:

  • Запустите событие yii\web\Response::EVENT_BEFORE_SEND.
  • Вызовите yii\web\Response::prepare(), чтобы отформатировать ответные данные в ответном контенте.
  • Запустите событие yii\web\Response::EVENT_AFTER_PREPARE.
  • Вызовите yii\web\Response::sendHeaders(), чтобы отправить зарегистрированные HTTP-заголовки.
  • Вызовите yii\web\Response::sendContent(), чтобы отправить содержимое тела ответа.
  • Запустите событие yii\web\Response::EVENT_AFTER_SEND.

После того, как yii\web\Response::send() вызывается один раз, дальнейший вызов этого метода будет проигнорирован. Это означает, что как только ответ будет отправлен, вы не сможете добавить к нему больше контента.

Как вы можете видеть, метод yii\web\Response::send() запускает несколько полезных событий. Отвечая на эти события, можно настроить или украсить ответ.