响应

你的 Slim 应用的路由和中间件得到了一个 PSR-7 响应对象,该对象表示返回给客户端的当前 HTTP 响应。响应对象实现了 PSR-7 ResponseInterface,可以使用它来检查和操作 HTTP 响应状态、标头和正文。

如何获取响应对象

PSR-7 响应对象将注入到 Slim 应用程序路由中,作为路由回调的第二个参数,如下所示

<?php

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

$app->get('/hello', function (Request $request, Response $response) {
    $response->getBody()->write('Hello World');
    return $response;
});

$app->run();
图 1:将 PSR-7 响应注入到应用程序路由回调中。

响应状态

每个 HTTP 响应都有一个数字的 状态代码。状态代码标识要返回给客户端的 HTTP 响应类型。PSR-7 响应对象的默认状态代码为 200 (OK)。你可以使用 getStatusCode() 方法获取 PSR-7 响应对象的状态代码,如下所示。

$status = $response->getStatusCode();
图 3:获取响应状态代码。

可以复制一个 PSR-7 响应对象,并像这样指定一个新的状态代码

$newResponse = $response->withStatus(302);
图 4:创建具有新状态代码的响应。

响应标头

每个HTTP响应都有头信息。这些是描述HTTP响应的元数据,但不会响应正文中显示。PSR-7 Response对象提供了几个方法来检查和操作其头信息。

获取所有头信息

您可以使用 PSR-7 Response 对象的 getHeaders() 方法以关联数组的形式获取所有 HTTP 响应头信息。结果关联数组的键是头信息名称,其值本身是其各自头信息名称的字符串值的数字数组。

$headers = $response->getHeaders();
foreach ($headers as $name => $values) {
    echo $name . ": " . implode(", ", $values);
}
图示 5:以关联数组的形式获取并迭代所有 HTTP 响应头信息。

获取一个头信息

您可以使用 PSR-7 Response 对象的 getHeader($name) 方法获取单个头信息的多个值。这将返回给定头信息名称的值数组。记住,单个 HTTP 头信息可能有多个值!

$headerValueArray = $response->getHeader('Vary');
图示 6:获取特定 HTTP 头信息的多个值。

您还可以使用 PSR-7 Response 对象的getHeaderLine($name) 方法获取具有给定头信息的所有值的逗号分隔字符串。与getHeader($name) 方法不同,此方法将返回一个逗号分隔字符串。

$headerValueString = $response->getHeaderLine('Vary');
图示 7:将单个头信息的多个值作为逗号分隔字符串获取。

检测头信息

您可以使用 PSR-7 Response 对象的 hasHeader($name) 方法测试是否存在头信息。

if ($response->hasHeader('Vary')) {
    // Do something
}
图示 8:检测特定 HTTP 头信息的存在。

设置头信息

您可以使用 PSR-7 Response 对象的 withHeader($name, $value) 方法设置头信息值。

$newResponse = $oldResponse->withHeader('Content-type', 'application/json');
图示 9:设置 HTTP 头信息。
提醒
Response 对象是不可变的。此方法将返回 Response 对象的副本,该副本具有新的头信息值。此方法具有破坏性,它会替换已与同一头信息名称关联的现有头信息值。

追加头信息

您可以使用 PSR-7 Response 对象的 withAddedHeader($name, $value) 方法追加头信息值。

$newResponse = $oldResponse->withAddedHeader('Allow', 'PUT');
图示 10:追加 HTTP 头信息。
提醒
withHeader() 方法不同,此方法将新值追加到同一头信息名称已存在的多个值中。Response 对象是不可变的。此方法将返回 Response 对象的副本,该副本具有已追加的头信息值。

移除头信息

您可以使用 Response 对象的 withoutHeader($name) 方法移除头信息。

$newResponse = $oldResponse->withoutHeader('Allow');
图示 11:移除 HTTP 头信息。
提醒
Response 对象是不可变的。此方法将返回 Response 对象的副本,该副本不包含指定的头信息。

响应正文

HTTP 响应通常具有正文。

与 PSR-7 请求对象一样,PSR-7 响应对象将主体实现为 Psr\Http\Message\StreamInterface 的一个实例。您可以通过 PSR-7 响应对象的 getBody() 方法获取 HTTP 响应主体 StreamInterface 实例。如果传出的 HTTP 响应长度未知或对于可用的内存过大,则 getBody() 方法更为方便。

$body = $response->getBody();
图 12:获取 HTTP 响应主体

产生的 Psr\Http\Message\StreamInterface 实例提供以下方法来读取、迭代并写入其底层的 PHP resource

  • getSize()
  • tell()
  • eof()
  • isSeekable()
  • seek()
  • rewind()
  • isWritable()
  • write($string)
  • isReadable()
  • read($length)
  • getContents()
  • getMetadata($key = null)

大多数情况下,您需要写入 PSR-7 响应对象。您可以通过其 write() 方法将内容写入 StreamInterface 实例,如下所示

$body = $response->getBody();
$body->write('Hello');
图 13:将内容写入 HTTP 响应主体

您还可以使用完全新的 StreamInterface 实例替换 PSR-7 响应对象的正文。当您希望将内容从远程目的地(例如文件系统或远程 API)输入到 HTTP 响应中时,这尤其有用。您可以使用其 withBody(StreamInterface $body) 方法替换 PSR-7 响应对象的正文。它的参数 必须Psr\Http\Message\StreamInterface 的一个实例。

use GuzzleHttp\Psr7\LazyOpenStream;

$newStream = new LazyOpenStream('/path/to/file', 'r');
$newResponse = $oldResponse->withBody($newStream);
图 14:替换 HTTP 响应主体
提醒
响应对象是不可变的。此方法返回包含新正文的响应对象的副本

返回 JSON

最简单的形式是,JSON 数据可以通过默认 200 HTTP 状态代码返回。

$data = array('name' => 'Bob', 'age' => 40);
$payload = json_encode($data);

$response->getBody()->write($payload);
return $response
          ->withHeader('Content-Type', 'application/json');
图 15:使用 200 HTTP 状态代码返回 JSON。

我们还可以使用自定义 HTTP 状态代码返回 JSON 数据。

$data = array('name' => 'Rob', 'age' => 40);
$payload = json_encode($data);

$response->getBody()->write($payload);
return $response
          ->withHeader('Content-Type', 'application/json')
          ->withStatus(201);
图 16:使用 201 HTTP 状态代码返回 JSON。
提醒
响应对象是不可变的。此方法返回一个有新的内容类型标题的响应对象的副本此方法是破坏性的,它替换现有的内容类型标题。

返回重定向

您可以使用 Location 标题重定向 HTTP 客户端。

return $response
  ->withHeader('Location', 'https://www.example.com')
  ->withStatus(302);
图 17:返回到 https://www.example.com 的重定向