runtime-responses.md 13.2 KB
Newer Older
pana1990 committed
1
Respuestas
2 3
==========

pana1990 committed
4
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.
5 6 7 8 9 10 11

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>

pana1990 committed
12
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:
13 14 15 16 17

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

pana1990 committed
18
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:
19 20 21 22 23

```php
throw new \yii\web\NotFoundHttpException;
```

pana1990 committed
24
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.
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53

* [[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');

pana1990 committed
54
// asigna una cabecera Pragma. Cualquier cabecera Pragma existente será descartada.
55
$headers->set('Pragma', 'no-cache');
56 57 58 59 60

// Elimina las cabeceras Pragma y devuelve los valores de las eliminadas en un array
$values = $headers->remove('Pragma');
```

pana1990 committed
61
>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()]].
62

pana1990 committed
63
## Cuerpo de la Respuesta<a name="response-body"></a>
64

pana1990 committed
65
La mayoría de las respuestas deben tener un cuerpo que contenga el contenido que se quiere mostrar a los usuarios finales.
66

pana1990 committed
67
Si ya se tiene un texto de cuerpo con formato, se puede asignar a la propiedad [[yii\web\Response::content]] del response. Por ejemplo:
68 69 70 71 72 73 74 75 76 77 78 79 80

```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'];
```

pana1990 committed
81
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]].
82 83 84 85 86 87

* [[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]].

pana1990 committed
88
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:
89 90 91 92 93 94 95 96

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

pana1990 committed
97
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.
98

pana1990 committed
99
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:
100 101 102 103 104 105 106 107 108 109 110 111

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

pana1990 committed
112
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:
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127

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

pana1990 committed
128
>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.
129

pana1990 committed
130
## Redirección del Navegador <a name="browser-redirection"></a>
131

pana1990 committed
132
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.
133

pana1990 committed
134
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:
135 136 137 138 139 140 141 142

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

pana1990 committed
143
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.
144 145 146 147 148 149 150

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();
```

pana1990 committed
151
> 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*.
152

pana1990 committed
153
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.
154

pana1990 committed
155
> 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.
156 157 158 159 160 161 162 163 164

## 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.

pana1990 committed
165
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.
166 167 168 169 170 171 172 173

```php
public function actionDownload()
{
    return \Yii::$app->response->sendFile('ruta/del/fichero.txt');
}
```

pana1990 committed
174
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.
175 176 177 178 179

```php
\Yii::$app->response->sendFile('ruta/del/fichero.txt')->send();
```

pana1990 committed
180
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.
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197

- 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.
pana1990 committed
198
5. Llama a [[yii\web\Response::sendContent()]] para enviar el contenido del cuerpo de la respuesta.
199 200
6. Lanza el evento [[yii\web\Response::EVENT_AFTER_SEND]].

pana1990 committed
201
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.
202

pana1990 committed
203
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.