# HTTP 響應
Slim 應用程序的路由和中間件給出了一個 PSR 7 響應對象,它表示當前的 HTTP 響應將被返回給客戶端。該響應對象遵循 [PSR 7 響應接口](http://www.php-fig.org/psr/psr-7/#3-2-1-psr-http-message-responseinterface)實現,因此你可以檢查和操作該 HTTP 響應的狀態、響應頭和響應體。
## 如何獲取 HTTP 響應對象
PSR 7 響應對象作為路由回調的第二個參數注入到 Slim 應用程序的路由中:
```
<?php
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
$app = new \Slim\App;
$app->get('/foo', function (ServerRequestInterface $request, ResponseInterface $response) {
// Use the PSR 7 $response object
return $response;
});
$app->run();
```
Figure 1: Inject PSR 7 response into application route callback.
PSR 7 請求對象作為中間件 callable 的第二個參數注入到 Slim 應用程序的_中間件_:
```
<?php
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
$app = new \Slim\App;
$app->add(function (ServerRequestInterface $request, ResponseInterface $response, callable $next) {
// Use the PSR 7 $response object
return $next($request, $response);
});
// Define app routes... $app->run();
```
Figure 2: Inject PSR 7 response into application middleware.
## HTTP 響應狀態
每個 HTTP 響應都有一個數字 [狀態編碼](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)。狀態編碼用于識別返回客戶端的 HTTP 響應的_類型_。PSR 7 響應對象的默認狀態編碼是 `200` (OK)。你可以像這樣使用 `getStatusCode()` 方法獲取 PSR 7 響應對象的狀態編碼:
```
$status = $response->getStatusCode();
```
Figure 3: Get response status code.
你可以拷貝一個 PSR 7 響應對象,并指定一個新的狀態編碼,像這樣:
```
$newResponse = $response->withStatus(302);
```
Figure 4: Create response with new status code.
## HTTP 響應頭
每個 HTTP 響應都有其相應的響應頭。這些元數據描述了 HTTP 響應,但在響應體中不可見。Slim 的 PSR 7 響應對象提供了幾種檢查和操作響應頭的方法。
### 獲取所有響應頭
使用 PSR 7 響應對象的 `getHeaders()` 方法提取所有 HTTP 響應頭并存入一個關聯數組中。該關聯數組的鍵名即為響應頭的名稱,鍵值是與其響應頭對應的值的字符串數組。
```
$headers = $response->getHeaders();
foreach ($headers as $name => $values) {
echo $name . ": " . implode(", ", $values);
}
```
Figure 5: Fetch and iterate all HTTP response headers as an associative array.
### 獲取單個響應頭
使用 PSR 7 響應對象的 `getHeader($name)` 方法獲取單個響應頭的值。它將返回指定響應頭的值組成的數組。記住,單個 HTTP 響應不止一個值。
```
$headerValueArray = $response->getHeader('Vary');
```
Figure 6: Get values for a specific HTTP header.
同樣,可以是 PSR 7 響應對象的 `getHeaderLine($name)` 方法獲取指定響應頭的所有值,由逗號隔開。不同于 `getHeader($name)` 方法,此方法返回的是由逗號隔開的字符串。
```
$headerValueString = $response->getHeaderLine('Vary');
```
Figure 7: Get single header's values as comma-separated string.
### 檢查響應頭
使用 PSR 7 響應對象的 `hasHeader($name)` 方法檢查響應頭存在與否。
```
if ($response->hasHeader('Vary')) {
// Do something
}
```
Figure 8: Detect presence of a specific HTTP header.
### 設置響應頭
使用 PSR 7 響應對象的 `withHeader($name, $value)` 方法設置響應頭的值。
```
$newResponse = $oldResponse->withHeader('Content-type', 'application/json');
```
Figure 9: Set HTTP header
**提示**響應對象是不可改的。此方法返回一個響應對象的_拷貝(copy)_,它擁有新的值。**此方法是破壞性的**,它替換了已有的同名響應頭現有的值。
### 追加響應頭/Append Header
使用 PSR 7 響應對象的 `withAddedHeader($name, $value)` 方法追加一個響應頭的值。
```
$newResponse = $oldResponse->withAddedHeader('Allow', 'PUT');
```
Figure 10: Append HTTP header
**提示**不同于 `withHeader()` 方法,此方法是_追加(append)_新的值到響應頭已有的值中。該響應對象是不可修改的。此方法返回一個已添加新值的該對象的_拷貝_。
### 移除響應頭
使用 HTTP 響應對象的 `withoutHeader($name)` 方法移除響應頭。
```
$newResponse = $oldResponse->withoutHeader('Allow');
```
Figure 11: Remove HTTP header
**提示**響應對象是不可改的。此方法返回一個帶有追加的響應頭的值的響應對象的_拷貝(copy)_。
## HTTP 響應體
HTTP 響應通常有一個響應體。Slim 提供了一個 PSR 7 響應對象,你可以用它檢查或操作可能會有的響應體。
類似 PSR 7 請求對象,PSR 7 響應對象將響應體作為`\Psr\Http\Message\StreamInterface` 的實例來實現。你可以使用 PSR 7 響應對象的 `getBody()` 方法來獲取 HTTP 響應體 `StreamInterface` 的實例。該 `getBody()` 方法完美適用于未知大小或對于可用內容來說太大的輸出(outgoing) HTTP 響應。
```
$body = $response->getBody();
```
Figure 12: Get HTTP response body
所得的 `\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');
```
Figure 13: Write content to the HTTP response body
你同樣可以完整新建一個 `StreamInterface` 實例來_替換_ PSR 7 響應對象的響應體。這玩意特別有用,尤其是你想要從遠程地址傳輸內容到 HTTP 響應中時(例如,文件系統或遠程API)。你可以使用 `withBody(StreamInterface $body)` 方法替換 PSR 7 響應對象的響應體。它的參數**必須**是 `\Psr\Http\Message\StreamInterface` 的實例。
```
$newStream = new \GuzzleHttp\Psr7\LazyOpenStream('/path/to/file', 'r');
$newResponse = $oldResponse->withBody($newStream);
```
Figure 14: Replace the HTTP response body
**提示**響應對象不可改變。這個方法返回的是包含新響應體的對象的_拷貝(copy)_。
## 返回 JSON
Slim 的響應對象擁有一個自定義方法 `withJson($data, $status, $encodingOptions)` 來幫助優化返回 JSON 數據的過程。
其中 `$data` 參數包含你希望返回的 JSON 的數據結構。`$status` 是可選的,并能返回一個自定義的 HTTP 代碼。`$encodingOptions` 是可選的,它是與[`json_encode()`](http://php.net/manual/en/function.json-encode.php) 相同的編碼選項。
最簡單的形式,可以返回帶默認的 200 HTTP 狀態代碼的 JSON 數據。
```
$data = array('name' => 'Bob', 'age' => 40);
$newResponse = $oldResponse->withJson($data);
```
Figure 15: Returning JSON with a 200 HTTP status code.
還可以返回帶有自定義 HTTP 狀態碼的 JSON 數據。
```
$data = array('name' => 'Rob', 'age' => 40);
$newResponse = $oldResponse->withJson($data, 201);
```
Figure 16: Returning JSON with a 201 HTTP status code.
HTTP 響應的 `Content-Type` 自動設置為 `application/json;charset=utf-8`。
如果 JSON 存在數據編碼問題,`\RuntimeException($message, $code)`將拋出異常,包含將 [`json_last_error_msg()`](http://php.net/manual/en/function.json-last-error-msg.php) 的值作為 `$message`的值以及將 [`json_last_error()`](http://php.net/manual/en/function.json-last-error.php) 作為 the `$code`的值。
**提示**響應對象是不可改的。此方法返回一個響應對象的_拷貝(copy)_,該拷貝帶有新的 Content-Type 頭。**該方法是毀滅性的**,它將_替換(replaces)_已存在的 Content-Type 頭。當 `withJson()` 被調用,如果傳遞了 $status ,HTTP 狀態碼也會被替換。