mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-22 17:48:37 +08:00
204 lines
13 KiB
Markdown
204 lines
13 KiB
Markdown
Respuestas
|
|
==========
|
|
|
|
Cuando una aplicación finaliza la gestión de una [petición (request)](runtime-requests.md), genera un objeto [[yii\web\Response|response]] y lo envía al usuario final. El objeto response contiene información tal como el código de estado (status code) HTTP, las cabeceras (headers) HTTP y el cuerpo (body). El objetivo final del desarrollo de una aplicación Web es esencialmente construir objetos response para varias peticiones.
|
|
|
|
En la mayoría de casos principalmente se debe tratar con [componentes de aplicación](structure-application-components.md) de tipo `response` que, por defecto, son una instancia de [[yii\web\Response]]. Sin embargo, Yii permite crear sus propios objetos `response` y enviarlos al usuario final tal y como se explica a continuación.
|
|
|
|
En esta sección, se describirá como generar y enviar respuestas a usuarios finales.
|
|
|
|
## Códigos de Estado <a name="status-code"></a>
|
|
|
|
Una de las primeras cosas que debería hacerse cuando se genera una respuesta es indicar si la petición se ha gestionado correctamente. Esto se indica asignando la propiedad [[yii\web\Response::statusCode]] a la que se le puede asignar cualquier valor válido dentro de los [códigos de estado HTTP](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html). Por ejemplo, para indicar que la petición se ha gestionado correctamente, se puede asignar el código de estado a 200, como en el siguiente ejemplo:
|
|
|
|
```php
|
|
Yii::$app->response->statusCode = 200;
|
|
```
|
|
|
|
Sin embargo, en la mayoría de casos nos es necesario asignar explícitamente el código de estado. Esto se debe a que el valor por defecto de [[yii\web\Response::statusCode]] es 200. Y si se quiere indicar que la petición ha fallado, se puede lanzar una excepción HTTP apropiada como en el siguiente ejemplo:
|
|
|
|
```php
|
|
throw new \yii\web\NotFoundHttpException;
|
|
```
|
|
|
|
Cuando el [error handler](runtime-handling-errors.md) captura una excepción, obtendrá el código de estado de la excepción y lo asignará a la respuesta. En el caso anterior, la excepción [[yii\web\NotFoundHttpException]] está asociada al estado HTTP 404. En Yii existen las siguientes excepciones predefinidas.
|
|
|
|
* [[yii\web\BadRequestHttpException]]: código de estado 400.
|
|
* [[yii\web\ConflictHttpException]]: código de estado 409.
|
|
* [[yii\web\ForbiddenHttpException]]: código de estado 403.
|
|
* [[yii\web\GoneHttpException]]: código de estado 410.
|
|
* [[yii\web\MethodNotAllowedHttpException]]: código de estado 405.
|
|
* [[yii\web\NotAcceptableHttpException]]: código de estado 406.
|
|
* [[yii\web\NotFoundHttpException]]: código de estado 404.
|
|
* [[yii\web\ServerErrorHttpException]]: código de estado 500.
|
|
* [[yii\web\TooManyRequestsHttpException]]: código de estado 429.
|
|
* [[yii\web\UnauthorizedHttpException]]: código de estado 401.
|
|
* [[yii\web\UnsupportedMediaTypeHttpException]]: código de estado 415.
|
|
|
|
Si la excepción que se quiere lanzar no se encuentra en la lista anterior, se puede crear una extendiendo [[yii\web\HttpException]], o directamente lanzando un código de estado, por ejemplo:
|
|
|
|
```php
|
|
throw new \yii\web\HttpException(402);
|
|
```
|
|
|
|
## Cabeceras HTTP <a name="http-headers"></a>
|
|
|
|
Se puede enviar cabeceras HTTP modificando el [[yii\web\Response::headers|header collection]] en el componente `response`. Por ejemplo:
|
|
|
|
```php
|
|
$headers = Yii::$app->response->headers;
|
|
|
|
// añade una cabecera Pragma. Las cabeceras Pragma existentes NO se sobrescribirán.
|
|
$headers->add('Pragma', 'no-cache');
|
|
|
|
// asigna una cabecera Pragma. Cualquier cabecera Pragma existente será descartada.
|
|
$headers->set('Pragma', 'no-cache');
|
|
|
|
// Elimina las cabeceras Pragma y devuelve los valores de las eliminadas en un array
|
|
$values = $headers->remove('Pragma');
|
|
```
|
|
|
|
>Información: Los nombres de las cabeceras case insensitive, es decir, no discriminan entre mayúsculas y minúsculas. Además, las nuevas cabeceras registradas no se enviarán al usuario hasta que se llame al método [[yii\web\Response::send()]].
|
|
|
|
## Cuerpo de la Respuesta<a name="response-body"></a>
|
|
|
|
La mayoría de las respuestas deben tener un cuerpo que contenga el contenido que se quiere mostrar a los usuarios finales.
|
|
|
|
Si ya se tiene un texto de cuerpo con formato, se puede asignar a la propiedad [[yii\web\Response::content]] del response. Por ejemplo:
|
|
|
|
```php
|
|
Yii::$app->response->content = 'hello world!';
|
|
```
|
|
|
|
Si se tiene que dar formato a los datos antes de enviarlo al usuario final, se deben asignar las propiedades [[yii\web\Response::format|format]] y [[yii\web\Response::data|data]]. La propiedad [[yii\web\Response::format|format]] especifica que formato debe tener [[yii\web\Response::data|data]]. Por ejemplo:
|
|
|
|
```php
|
|
$response = Yii::$app->response;
|
|
$response->format = \yii\web\Response::FORMAT_JSON;
|
|
$response->data = ['message' => 'hello world'];
|
|
```
|
|
|
|
Yii soporta a los siguientes formatos de forma predeterminada, cada uno de ellos implementado por una clase [[yii\web\ResponseFormatterInterface|formatter]]. Se pueden personalizar los formatos o añadir nuevos sobrescribiendo la propiedad [[yii\web\Response::formatters]].
|
|
|
|
* [[yii\web\Response::FORMAT_HTML|HTML]]: implementado por [[yii\web\HtmlResponseFormatter]].
|
|
* [[yii\web\Response::FORMAT_XML|XML]]: implementado por [[yii\web\XmlResponseFormatter]].
|
|
* [[yii\web\Response::FORMAT_JSON|JSON]]: implementado por [[yii\web\JsonResponseFormatter]].
|
|
* [[yii\web\Response::FORMAT_JSONP|JSONP]]: implementado por [[yii\web\JsonResponseFormatter]].
|
|
|
|
Mientras el cuerpo de la respuesta puede ser mostrado de forma explicita como se muestra a en el anterior ejemplo, en la mayoría de casos se puede asignar implícitamente por el valor de retorno de los métodos de [acción](structure-controllers.md). El siguiente, es un ejemplo de uso común:
|
|
|
|
```php
|
|
public function actionIndex()
|
|
{
|
|
return $this->render('index');
|
|
}
|
|
```
|
|
|
|
La acción `index` anterior, devuelve el resultado renderizado de la vista `index`. el valor devuelto será recogido por el componente `response`, se le aplicará formato y se enviará al usuario final.
|
|
|
|
Por defecto, el formato de respuesta es [[yii\web\Response::FORMAT_HTML|HTML]], sólo se debe devolver un string en un método de acción. Si se quiere usar un formato de respuesta diferente, se debe asignar antes de devolver los datos. por ejemplo:
|
|
|
|
```php
|
|
public function actionInfo()
|
|
{
|
|
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
|
|
return [
|
|
'message' => 'hello world',
|
|
'code' => 100,
|
|
];
|
|
}
|
|
```
|
|
|
|
Como se ha mencionado, además de usar el componente de aplicación `response` predeterminado, también se pueden crear objetos response propios y enviarlos a los usuarios finales. Se puede hacer retornando un objeto en el método de acción, como en el siguiente ejemplo:
|
|
|
|
```php
|
|
public function actionInfo()
|
|
{
|
|
return \Yii::createObject([
|
|
'class' => 'yii\web\Response',
|
|
'format' => \yii\web\Response::FORMAT_JSON,
|
|
'data' => [
|
|
'message' => 'hello world',
|
|
'code' => 100,
|
|
],
|
|
]);
|
|
}
|
|
```
|
|
|
|
>Nota: Si se crea un objeto response propio, no se podrán aprovechar las configuraciones asignadas para el componente `response` en la configuración de la aplicación. Sin embargo, se puede usar la [inyección de dependencias](concept-di-container.md) para aplicar la configuración común al nuevo objeto response.
|
|
|
|
## Redirección del Navegador <a name="browser-redirection"></a>
|
|
|
|
La redirección del navegador se basa en el envío de la cabecera HTTP `Location`. Debido a que esta característica se usa comúnmente, Yii proporciona soporte especial para ello.
|
|
|
|
Se puede redirigir el navegador a una URL llamando al método [[yii\web\Response::redirect()]]. El método asigna la cabecera de `Location` apropiada con la URL proporcionada y devuelve el objeto response él mismo. En un método de acción, se puede acceder a él mediante el acceso directo [[yii\web\Controller::redirect()]] como en el siguiente ejemplo:
|
|
|
|
```php
|
|
public function actionOld()
|
|
{
|
|
return $this->redirect('http://example.com/new', 301);
|
|
}
|
|
```
|
|
|
|
En el ejemplo anterior, el método de acción devuelve el resultado del método `redirect()`. Como se ha explicado antes, el objeto response devuelto por el método de acción se usará como respuesta enviándola al usuario final.
|
|
|
|
En otros sitios que no sean los métodos de acción, se puede llamar a [[yii\web\Response::redirect()]] directamente seguido por una llamada al método [[yii\web\Response::send()]] para asegurar que no habrá contenido extra en la respuesta.
|
|
|
|
```php
|
|
\Yii::$app->response->redirect('http://example.com/new', 301)->send();
|
|
```
|
|
|
|
> Información: De forma predeterminada, el método [[yii\web\Response::redirect()]] asigna el estado de respuesta al código de estado 302 que indica al navegador que recurso solicitado está *temporalmente* alojado en una URI diferente. Se puede enviar un código de estado 301 para expresar que el recurso se ha movido de forma *permanente*.
|
|
|
|
Cuando la petición actual es de una petición AJAX, el hecho de enviar una cabecera `Location` no causará una redirección del navegador automática. Para resolver este problema, el método [[yii\web\Response::redirect()]] asigna una cabecera `X-Redirect` con el valor de la URL de redirección. En el lado del cliente se puede escribir código JavaScript para leer la esta cabecera y redireccionar el navegador como corresponda.
|
|
|
|
> Información: Yii contiene el archivo JavaScript `yii.js` que proporciona un conjunto de utilidades comunes de JavaScript, incluyendo la redirección de navegador basada en la cabecera `X-Redirect`. Por tanto, si se usa este fichero JavaScript (registrándolo *asset bundle* [[yii\web\YiiAsset]]), no se necesitará escribir nada más para tener soporte en redirecciones AJAX.
|
|
|
|
## Enviar Archivos <a name="sending-files"></a>
|
|
|
|
Igual que con la redirección, el envío de archivos es otra característica que se basa en cabeceras HTTP especificas. Yii proporciona un conjunto de métodos para dar soporte a varias necesidades del envío de ficheros. Todos ellos incorporan soporte para el rango de cabeceras HTTP.
|
|
|
|
* [[yii\web\Response::sendFile()]]: envía un fichero existente al cliente.
|
|
* [[yii\web\Response::sendContentAsFile()]]: envía un string al cliente como si fuera un archivo .
|
|
* [[yii\web\Response::sendStreamAsFile()]]: envía un *file stream* existente al cliente como si fuera un archivo.
|
|
|
|
Estos métodos tienen la misma firma de método con el objeto response como valor de retorno. Si el archivo que se envía es muy grande, se debe considerar usar [[yii\web\Response::sendStreamAsFile()]] porque es más efectivo en términos de memoria. El siguiente ejemplo muestra como enviar un archivos en una acción de controlador.
|
|
|
|
```php
|
|
public function actionDownload()
|
|
{
|
|
return \Yii::$app->response->sendFile('ruta/del/fichero.txt');
|
|
}
|
|
```
|
|
|
|
Si se llama al método de envío de ficheros fuera de un método de acción, también se debe llamar al método [[yii\web\Response::send()]] después para asegurar que no se añada contenido extra a la respuesta.
|
|
|
|
```php
|
|
\Yii::$app->response->sendFile('ruta/del/fichero.txt')->send();
|
|
```
|
|
|
|
Algunos servidores Web tienen un soporte especial para enviar ficheros llamado *X-Sendfile*. La idea es redireccionar la petición para un fichero a un servidor Web que servirá el fichero directamente. Como resultado, la aplicación Web puede terminar antes mientras el servidor Web envía el fichero. Para usar esta funcionalidad, se puede llamar a [[yii\web\Response::xSendFile()]]. La siguiente lista resume como habilitar la característica `X-Sendfile` para algunos servidores Web populares.
|
|
|
|
- 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)
|
|
|
|
## Enviar la Respuesta <a name="sending-response"></a>
|
|
|
|
El contenido en una respuesta no se envía al usuario hasta que se llama al método [[yii\web\Response::send()]]. De forma predeterminada, se llama a este método automáticamente al final de [[yii\base\Application::run()]]. Sin embargo, se puede llamar explícitamente a este método forzando el envío de la respuesta inmediatamente.
|
|
|
|
El método [[yii\web\Response::send()]] sigue los siguientes pasos para enviar una respuesta:
|
|
|
|
1. Lanza el evento [[yii\web\Response::EVENT_BEFORE_SEND]].
|
|
2. Llama a [[yii\web\Response::prepare()]] para convertir el [[yii\web\Response::data|response data]] en [[yii\web\Response::content|response content]].
|
|
3. Lanza el evento [[yii\web\Response::EVENT_AFTER_PREPARE]].
|
|
4. Llama a [[yii\web\Response::sendHeaders()]] para enviar las cabeceras HTTP registradas.
|
|
5. Llama a [[yii\web\Response::sendContent()]] para enviar el contenido del cuerpo de la respuesta.
|
|
6. Lanza el evento [[yii\web\Response::EVENT_AFTER_SEND]].
|
|
|
|
Después de llamar a [[yii\web\Response::send()]] por primera vez, cualquier llamada a este método será ignorada. Esto significa que una vez se envíe una respuesta, no se le podrá añadir más contenido.
|
|
|
|
Como se puede observar, el método [[yii\web\Response::send()]] lanza varios eventos útiles. Al responder a estos eventos, es posible ajustar o decorar la respuesta.
|