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

larnu committed
4 5 6 7
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.
8

larnu committed
9 10 11 12
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.
13 14 15 16 17

En esta sección, se describirá como generar y enviar respuestas a usuarios finales.

## Códigos de Estado <a name="status-code"></a>

larnu committed
18 19 20 21 22
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:
23 24 25 26 27

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

larnu committed
28 29 30
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:
31 32 33 34 35

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

larnu committed
36 37 38
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.
39 40 41 42 43 44 45 46 47 48 49 50 51

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

larnu committed
52 53
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:
54 55 56 57 58 59 60

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

## Cabeceras HTTP <a name="http-headers"></a> 

larnu committed
61 62
Se puede enviar cabeceras HTTP modificando el [[yii\web\Response::headers|header collection]] en el componente 
`response`. Por ejemplo:
63 64 65 66 67 68 69

```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
70
// asigna una cabecera Pragma. Cualquier cabecera Pragma existente será descartada.
71
$headers->set('Pragma', 'no-cache');
72 73 74 75 76

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

larnu committed
77 78 79
> 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()]].
80

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

larnu committed
83 84
La mayoría de las respuestas deben tener un cuerpo que contenga el contenido que se quiere mostrar a los usuarios 
finales.
85

larnu committed
86 87
Si ya se tiene un texto de cuerpo con formato, se puede asignar a la propiedad [[yii\web\Response::content]] del 
response. Por ejemplo:
88 89 90 91 92

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

larnu committed
93 94 95
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:
96 97 98 99 100 101 102

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

larnu committed
103 104 105
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]].
106 107 108 109 110 111

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

larnu committed
112 113 114
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:
115 116 117 118 119 120 121 122

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

larnu committed
123 124
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.
125

larnu committed
126 127 128
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:
129 130 131 132 133 134 135 136 137 138 139 140

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

larnu committed
141 142 143
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:
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158

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

larnu committed
159 160 161
> 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.
162

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

larnu committed
165 166
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.
167

larnu committed
168 169 170 171
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:
172 173 174 175 176 177 178 179

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

larnu committed
180 181
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.
182

larnu committed
183 184 185
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.
186 187 188 189 190

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

larnu committed
191 192 193
> 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*.
194

larnu committed
195 196 197 198
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.
199

larnu committed
200 201 202 203
> 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.
204 205 206

## Enviar Archivos <a name="sending-files"></a>

larnu committed
207 208 209
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.
210 211

* [[yii\web\Response::sendFile()]]: envía un fichero existente al cliente.
larnu committed
212
* [[yii\web\Response::sendContentAsFile()]]: envía un string al cliente como si fuera un archivo.
213 214
* [[yii\web\Response::sendStreamAsFile()]]: envía un *file stream* existente al cliente como si fuera un archivo.

larnu committed
215 216 217
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 archivo en una acción de controlador.
218 219 220 221 222 223 224 225

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

larnu committed
226 227
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.
228 229 230 231 232

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

larnu committed
233 234 235 236 237
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.
238 239 240 241 242 243 244 245 246

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

larnu committed
247 248 249
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.
250 251 252 253

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]].
larnu committed
254 255
2. Llama a [[yii\web\Response::prepare()]] para convertir el [[yii\web\Response::data|response data]] en 
   [[yii\web\Response::content|response content]].
256 257
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
258
5. Llama a [[yii\web\Response::sendContent()]] para enviar el contenido del cuerpo de la respuesta.
259 260
6. Lanza el evento [[yii\web\Response::EVENT_AFTER_SEND]].

larnu committed
261 262
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.
263

larnu committed
264 265
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.