回應

你的 Slim 應用程式路由和中介軟體會給予 PSR-7 回應物件,用於表示要傳回給客戶端的目前 HTTP 回應。回應物件會實作 PSR-7 ResponseInterface,你可以使用它來檢查和處理 HTTP 回應狀態、標頭和本文。

如何取得 Response 物件

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 Response 物件的預設狀態碼是 200(OK)。你可以使用 getStatusCode() 方法取得 PSR-7 Response 物件的狀態碼,如下所示。

$status = $response->getStatusCode();
圖 3:取得回應狀態碼。

你可以複製 PSR-7 Response 物件並指定新的狀態碼,如下所示

$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 Request 物件一樣,PSR-7 Response 物件會將本體實作為 Psr\Http\Message\StreamInterface 的一個執行個體。您可以使用 PSR-7 Response 物件的 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 Response 物件。您可以使用 StreamInterface 執行個體的 write() 方法寫入內容,就像這樣

$body = $response->getBody();
$body->write('Hello');
圖 13:寫入內容到 HTTP 回應本體

您也可以用一個全新的 StreamInterface 執行個體替換 PSR-7 Response 物件的本體。這特別適用於當您想要將遠端目的地(例如檔案系統或遠端 API)的內容傳輸到 HTTP 回應中時。您可以使用 withBody(StreamInterface $body) 方法替換 PSR-7 Response 物件的本體。它的引數必須Psr\Http\Message\StreamInterface 的一個執行個體。

use GuzzleHttp\Psr7\LazyOpenStream;

$newStream = new LazyOpenStream('/path/to/file', 'r');
$newResponse = $oldResponse->withBody($newStream);
圖 14:替換 HTTP 回應本體
提醒
Response 物件是不可變的。此方法會傳回一個 Response 物件的副本,其中包含新的本體。

傳回 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。
提醒
Response 物件是不可變的。此方法會傳回一個 Response 物件的副本,其中具有新的 Content-Type 標頭。此方法具有破壞性,而它會替換現有的 Content-Type 標頭。

傳回重新導向

您可以透過使用 Location 標頭重新導向 HTTP 客戶端。

return $response
  ->withHeader('Location', 'https://www.example.com')
  ->withStatus(302);
圖 17:傳回重新導向到 https://www.example.com