CakePHP 2.x CookBook 中文版 第五章 控制器 之 請求和響應對象

 

請求和響應對象

在 CakePHP 2.0 中請求和響應對象是新的。在之前的版本中,這些對象是用數組來表示的,相關的方法分散在RequestHandlerComponentRouter、 Dispatcher 和 Controller 中。請求包含的信息上沒有認證對象。在 2.0 中,CakeRequest 和 CakeResponse 用於此目的。

CakeRequest

CakeRequest 是 CakePHP 中的默認請求對象。它在請求數據中集成了一些諮詢和交互特性。 CakeReqeust 建立在每個請求上,並以引用方式傳遞給使用請求數據的各個層。默認情況下,CakeRequest 賦值給 $this->request,並且在控制器、視圖和助手中可用。 CakeRequest 執行的部分職責包括:

  • 將 GET、POST 和 FILES 數組處理成你熟悉的數據結構。
  • 爲請求提供相關的自省環境。類似於頭信息傳送、客戶端 IP 地址、運行應用程序的服務器的子域/域信息等。
  • 提供以數組成員/對象屬性兩種形式訪問請求參數。

訪問請求對象

CakeRequest 提供了數種訪問請求參數的接口。一是使用對象屬性,二是使用數組索引,三是通過 $this->request->params

$this->request->controller;
$this->request['controller'];
$this->request->params['controller'];

上面的所有形式都訪問同一個值。提供參數的多種訪問形式是爲了已有應用程序的遷移。所有的 路由元素 都可以通過這種形式訪問。

除了 路由元素 ,還經常需要訪問 傳遞參數 和 命名參數。這此在請求對象中也都可用:

// 傳遞的參數
$this->request->pass;
$this->request['pass'];
$this->request->params['pass'];

// 命名的參數
$this->request->named;
$this->request['named'];
$this->request->params['named'];

上面提供了傳遞參數和命名參數的訪問。這裏有幾個 CakePHP 內部使用的重要/有用的參數,這些參數也能在請求參數中找到:

  • plugin 處理請求的插件,沒有插件時爲空(null)。
  • controller 處理當前請求的控制器。
  • action 處理當前請求的動作。
  • prefix 當前動作的前綴。更多信息參見 前置路由 。
  • bare 在請求來自 requestAction() 並且包含 bare 選項時出現。Bare 請求沒有要渲染的佈局。
  • requested 當動作來自 requestAction 時出現並被設置爲 true。

訪問 query 參數

query 參數可以用 CakeRequest::$query 讀取:

// url 爲 /posts/index?page=1&sort=title
$this->request->query['page'];

// 也可以用數組形式來訪問
$this->request['url']['page']; // BC 存取器,將在未來版本中被廢棄

你也可以直接訪問 query 屬性,或者以容錯方式使用 CakeRequest::query() 讀取 url query 數組。 任何不存在的鍵都返回空(null)值:

$foo = $this->request->query('value_that_does_not_exist');
// $foo === null

訪問 POST 數據

所有的 POST 數據都可以用 CakeRequest::$data 訪問。包含 data 前綴的任意數據,其數據前綴都會被刪除。例如:

// An input with a name attribute equal to 'data[MyModel][title]' is accessible at
$this->request->data['MyModel']['title'];

你也可以直接訪問 data 屬性,或者以容錯方式使用 CakeRequest::data() 讀取 data 數組。 任何不存在的鍵都返回空(null)值:

$foo = $this->request->data('Value.that.does.not.exist');
// $foo == null

訪問 PUT 或者 POST 數據

2.2 新版功能.

在創建 REST 服務時,常常在 PUT 和 DELETE 接受請求數據。2.2 版中,所有 application/x-www-form-urlencoded請求的主體數據將自動解析並且設置爲 $this->data 的 PUT 和 DELETE 請求。如果你接收 JSON 或者 XML 數據,參看後文中如何訪問這些請求的部分。

訪問 XML 或者 JSON 數據

應用程序經常使用 REST 非 URL 編碼的 post 體之間交換數據。你可以以任意格式使用 CakeRequest::input() 訪問 input 數據。 通過提供解碼功能,你可以接受反序列化形式的內容:

// 獲取提交給 PUT/POST 動作的 JOSN 編碼數據
$data = $this->request->input('json_decode');

自從某些反序列化方法在被調用時包含附加參數, CakeRequest::input() 也支持在附加參數中傳遞 json_decode 中的 ‘as array’ 參數,或者如果你想將 XML 轉換成 DOM 文檔對象:

// Get Xml encoded data submitted to a PUT/POST action
$data = $this->request->input('Xml::build', array('return' => 'domdocument'));

訪問路徑信息

CakeRequest 還提供關於應用程序路徑的有用信息。 CakeRequest::$base 和 CakeRequest::$webroot 用於生成 url,並確定應用程序是否在一個子文件夾內。

檢查請求

以前使用 RequestHandlerComponent 檢測各種請求條件。這些方法已經被移到 CakeRequest,並且新的接口(包含多個與回調兼容的用法):

$this->request->is('post');
$this->request->isPost();

這些方法調用返回相同的值。眼下 RequestHandler 中的方法仍然可用,但是已經被廢棄並可能會在後續版本中刪除。你也可以輕易地擴展有效的請求探察器,使用 CakeRequest::addDetector() 創建新類型的探察器。你可以建立四種不同類型的探察器:

  • 環境值對比 - 一個環境值對比,對比來自 env() 的值和環境變量中已知的值是否來檢測所提供的值是否相等。
  • 模式值對比 - 模式值對比允許你對比來自 env() 的值和正則表達式。
  • 基於選項的對比 - 基於選項的對比使用一個選項列表建立一個正則表達式。後續調用加入一個已經定義選項的探察器來合併選項。
  • 回調探察器 - 回調發現都允許你提供一個 ‘回調’ 類型來處理檢測。回調將接受請求對象作爲它的唯一參數。

這有一些例子:

// 添加環境探察器。
$this->request->addDetector('post', array('env' => 'REQUEST_METHOD', 'value' => 'POST'));

// 添加模式值探察器。
$this->request->addDetector('iphone', array('env' => 'HTTP_USER_AGENT', 'pattern' => '/iPhone/i'));

// 添加選項探察器。
$this->request->addDetector('internalIp', array(
    'env' => 'CLIENT_IP',
    'options' => array('192.168.0.101', '192.168.0.100')
));

// 添加回調探察器。可以是匿名函數或者定時調用。
$this->request->addDetector('awesome', array('callback' => function ($request) {
    return isset($request->awesome);
}));

CakeRequest 還包括 CakeRequest::domain()、 CakeRequest::subdomains() 和 CakeRequest::host() 等方法用來幫助帶有子域的應用程序,讓生命更簡單。

下面是一些可用的內置探察器:

  • is('get') 檢測當前的請求是否是一個 GET。
  • is('put') 檢測當前的請求是否是一個 PUT.
  • is('post') 檢測當前的請求是否是一個 POST.
  • is('delete') 檢測當前的請求是否是一個 DELETE.
  • is('head') 檢測當前的請求是否是一個 HEAD.
  • is('options') C檢測當前的請求是否是 OPTIONS.
  • is('ajax') 檢測當前的請求是否是來自 X-Requested-with = XmlHttpRequest.
  • is('ssl') 檢測當前的請求是否經過 SSL
  • is('flash') 檢測當前的請求是否有一個 Flash 用戶代理
  • is('mobile') 檢測當前的請求是否來自移動代理。

CakeRequest 和 RequestHandlerComponent

由於``CakeRequest`` 提供的過去用於 RequestHandlerComponent 的一些特性,被要求反思如何仍然適合圖片。對於 2.0, RequestHandlerComponent 扮演了一個甜心爹爹的角色。在 CakeRequest 提供的功能的頂層掛了一層糖。所謂的糖就是在 RequestHandlerComponent 範圍內,基於內容的類型或者 ajax 切換佈局和視圖。這種分隔兩類的工具和糖,讓你更易於區分和選擇你想要的和你需要的。

與其它層面的請求互動

你可以使用 CakeRequest 反觀關於請求的各種事物。除了探察器,你還能找到來自各種屬性和方法的其他信息。

  • $this->request->webroot 表示 web 根目錄。
  • $this->request->base 表示基目錄。
  • $this->request->here 表示當前請求的完整地址。
  • $this->request->query 包含 query 字符串參數。

CakeRequest API

class CakeRequest

CakeRequest 壓制 request 參數句柄和反視。

CakeRequest::domain($tldLength = 1)

返回正在運行的應用程序的域。

CakeRequest::subdomains($tldLength = 1)

以數組形式返回應用程序的子域。

CakeRequest::host()

返回應用程序的主機。

CakeRequest::method()

返回請求所用的 HTTP 方式。

CakeRequest::onlyAllow($methods)

設置允許的 HTTP 方式,如果不匹配則拋出 MethodNotAllowexException。 這個伴隨傳送方法的 405 輸出包含必須的 ‘Allow’ 頭。 The 405 response will include the required ‘Allow’ header with the passed methods

2.3 新版功能.

CakeRequest::referer($local = false)

返回請求的來源地址。

CakeRequest::clientIp($safe = true)

返回當前訪問者的 IP 地址。

CakeRequest::header($name)

允許你使用此請求訪問任意的 HTTP_* 頭。

$this->request->header('User-Agent');

將返回此請求使用的用戶代理。

CakeRequest::input($callback[$options])

從請求中獲取 input 數據,並且可以向其傳遞一個解碼函數。附加的參數中的解碼函數可以被作爲傳遞給 input()。

CakeRequest::data($name)

提供 點表示法 訪問請求數據。允許讀取和修改請求數據,調用也可以串聯在一起:

// 修改部分請求數據,以使你能夠預一些表單域。
$this->request->data('Post.title', 'New post')
    ->data('Comment.1.author', 'Mark');

// 你也能讀出數據。
$value = $this->request->data('Post.title');
CakeRequest::query($name)

提供 點表示法 訪問 url query 數據:

// url 爲 /posts/index?page=1&sort=title
$value = $this->request->query('page');

2.3 新版功能.

CakeRequest::is($type)

檢查一個請求是否符合某個標準。應用內置的匹配規則或者通過 CakeRequest::addDetector() 定義的任意附加規則。

CakeRequest::addDetector($name$options)

添加一個用於 is() 的探察器。更多信息參見 檢查請求 。

CakeRequest::accepts($type = null)

找出客戶端接受的內容類型,或者檢測它們接受的特定內容的類型。

獲取全部類型

$this->request->accepts();

檢查單個類型

$this->request->accepts('application/json');
static CakeRequest::acceptLanguage($language = null)

獲取客戶端期望的全部語言,或者檢測被接受的一個特定語言。

獲取被接受的語言的列表

CakeRequest::acceptLanguage();

檢測一個指定的被接受的語言

CakeRequest::acceptLanguage('es-es');
property CakeRequest::$data

POST 數據的數組。作爲防止錯誤提示的一種方式,你可以使用 CakeRequest::data() 讀取這個屬性。

property CakeRequest::$query

query 字符串參數數組。

property CakeRequest::$params

路由元素和請求參數數組。

property CakeRequest::$here

返回當前請求的 uri。

property CakeRequest::$base

應用程序的基路徑,通常是 /,除非你的應用程序放在子目錄中。

property CakeRequest::$webroot

當前 web 根目錄

CakeResponse

CakeResponse 是 CakePHP 中的默認響應。它包含一些在應用程序中生成 HTTP 響應的特性和功能。它也有助於測試,因爲它能被 mock/stub以允許你檢查將要傳輸的頭信息。 與 CakeRequest 類似, CakeResponse 整合了一些原來放在 ControllerRequestHandlerComponent 和 Dispatcher 中的方法。那些舊方法已經被廢棄,轉而使用CakeResponse.。 CakeResponse 提供了一個接口,這個接口打包了與下面的任務相關的公用響應:

  • 爲跳轉傳送頭信息。
  • 傳送內容類型頭信息。
  • 傳送信息頭信息。
  • 傳送響應內容。

變化中的響應類

CakePHP 默認使用 CakeResponse。 CakeResponse 對於使用類是靈活透明的。不過如果你需要在應用程序的特定類中替換它,你使用自己的類覆蓋或替換 CakeResponse 。通過替換在 index.php 中使用的 CakePHPResponse。

這會使你的應用程序中的所有控制器都用 CustomResponse 代替 CakeResponse。 你也可以在控制器中設置 $this->response 來替換 response 實例。 在測試過程中覆蓋這個 response 對象很有用,它允許你 stub 與 header() 一起工作的方法。 更多的信息請參見 CakeResponse 與測試 一節。

處理內容類型

你可以使用 CakeResponse::type() 控制應用程序響應的 內容類型(Content-Type)。如果你的應用程序需要處理沒有內置在 CakeResponse 中的內容類型,你還可以使用 type() 映射它們:

// 添加 vCard 類型
$this->response->type(array('vcf' => 'text/v-card'));

// 將響應的 Content-Type 設置爲 vcard.
$this->response->type('vcf');

通常你將會在控制器的 beforeFilter 回調中映射附加的內容類型,如果需要,你可以利用 RequestHandlerComponent的自動視圖切換功能。

傳輸文件

有時候你需要爲請求發送文件作爲對其的響應。 在 2.3 版之前你可以使用 媒體視圖 來實現。 在 2.3 版,MediaView 被廢棄,現在你可以使用 CakeResponse::file() 傳送文件來響應:

public function sendFile($id) {
    $file = $this->Attachment->getFile($id);
    $this->response->file($file['path']);
}

上面顯示的例子要求向方法傳送文件路徑。 如果它是列在 CakeReponse::$_mimeTypes 中的已知文件類型,Cake 將發送適當的內容類型頭信息。你也可以在調用 CakeResponse::file() 之前使用 CakeResponse::type() 方法添加新的文件類型。

如果你只想讓文件顯示在瀏覽器中,禁止文件被下載,可以指定如下選項:

$this->response->file($file['path'], array('download' => true, 'name' => 'foo'));

與瀏覽器緩存交互

有時你需要禁止瀏覽器緩存控制器動作的結果。 CakeResponse::disableCache() 就是用來幹這個的::

public function index() {
    // do something.
    $this->response->disableCache();
}

警告

在 SSL 域中使用與下載文件一起使用 disableCache() 傳送文件到 Internet Explorer 可能引起錯誤。

你也可以使用 CakeResponse::cache() 告訴客戶端你想要它們緩存響應:

public function index() {
    //do something
    $this->response->cache('-1 minute', '+5 days');
}

上面的代碼告訴客戶端緩存響應結果5天,希望提高訪客在速度方面的體驗。cache() 的第一個參數是最後編輯(Last-Modifield)的值。過期(Expires)和最大年齡(Max-age)參數是以秒爲單位的。還有,緩存控制(Cache-Control)設置爲 public。

HTTP 緩存微調

提高應用程序的訪問速度的一個又好又容易的辦法是使用 HTTP 緩存。 在這種模式下,你只需要通過設置幾個 header (如編輯時間、響應實體及其它)來幫助客戶端決定是否使用響應的緩存副本。

反對使用代碼邏輯處理 緩存和在數據改變時失效(刷新),HTTP 的過期和校驗兩種模式常常比自己管理緩存更簡單。

除了 CakeResponse::cache() ,你還可以使用另外一些方法實現 HTTP 緩存頭來利用瀏覽器或反向代理緩存。

緩存控制 頭信息

2.1 新版功能.

在過期模式下,header 包括多個能夠使瀏覽器或代理使用緩存內容的標誌。一個 Cache-Control header 如下:

Cache-Control: private, max-age=3600, must-revalidate

CakeResponse 類及一些產生最終有效的 Cache-Control header 工具方法幫助你設置這些頭信息。其中的第一個是CakeResponse::sharable() 方法, 它標誌一個響應是否考慮在不同的用戶或者客戶端共享。 此方法實際控制 header 的 public 或者 private 部分。將響應設置成 private 標誌着它的全部或部分是設計給單個用戶的。要共享緩存需要將 control 指令設置爲 public。

此方法的第二個參數用於指定緩存的 Max-age ,它聲明響應在多少秒後過期。

public function view() {
    ...
    // set the Cache-Control as public for 3600 seconds
    $this->response->sharable(true, 3600);
}

public function my_data() {
    ...
    // set the Cache-Control as private for 3600 seconds
    $this->response->sharable(false, 3600);
}

CakeResponse 公開了設定 Cache-Control header 中的每個組件的獨立方法。

過期頭信息

2.1 新版功能.

還是在過期模式下,你可以設置 Expires header,它指定響應在指定的日期/時間之後過期,時間和日期爲 HTTP 規範格式。這個 header 可以使用 CakeResponse::expires() 方法設置:

public function view() {
    $this->response->expires('+5 days');
}

這個方法還接收一個 DateTime 或者能夠被 DateTime 類解析的任意字符串。

Etag 頭信息

2.1 新版功能.

HTTP 中的緩存校驗通常用於內容不間斷地更新的情況下,並且僅在緩存過期時通知應用程序生成響應內容。在這種模式下,客戶端持續在緩存中存儲頁面,但是不直接使用它,而是每次嚮應用程序諮詢資源是否被改變。它通常用於圖片或者其它靜態資源。

Etag header (調用實體 tag)是請求資源的唯一標識字符串。它非常像文件的檢驗和,緩存比較校驗和,確定它們是否匹配。

要想享受到使用這個 header 的好處,你還必須手動調用 CakeResponse::checkNotModified() 方法,或者在應用程序中包含 RequestHandlerComponent

public function index() {
    $articles = $this->Article->find('all');
    $this->response->etag($this->Article->generateHash($articles));
    if ($this->response->checkNotModified($this->request)) {
        return $this->response;
    }
    ...
}

最後編輯頭信息

2.1 新版功能.

還是在 HTTP 緩存校驗模式下,你能夠設置 Last-Modified header 來標誌一個資源最後一次被修改的日期和時間。設置這個 header 幫助 CakePHP 回覆緩存客戶端,基於客戶端緩存的響應是否被編輯了。

要享受到使用這個 header 的好處,你還必須手動調用 CakeResponse::checkNotModified() 方法,或者在應用程序中包含 RequestHandlerComponent ::

public function view() {
    $article = $this->Article->find('first');
    $this->response->modified($article['Article']['modified']);
    if ($this->response->checkNotModified($this->request)) {
        return $this->response;
    }
    ...
}

Vary 頭信息

某些情況下,你可能需要使用相同的 url 提供不同的內容。 通常是由於你需要根據瀏覽器請求的資源的不同爲其準備多個頁面或者不同的 HTML 迴應。你可以使用 Vary header 來應對這種情況:

$this->response->vary('User-Agent');
$this->response->vary('Accept-Encoding', 'User-Agent');
$this->response->vary('Accept-Language');

CakeResponse 與測試

CakeResponse 的最大的勝利也許是來自它使測試控制器和組件變得更容易了。代替遍佈多個對象的方法,僅使用一個 mock 對象作爲委託給控制器和組件的 CakeResponse 。這會幫助你走近 ‘unit’ 測試並使測試控制器更容易:

public function testSomething() {
    $this->controller->response = $this->getMock('CakeResponse');
    $this->controller->response->expects($this->once())->method('header');
    // ...
}

除此之外,從命令行運行測試也變得更容易了,你可以使用 mock 避開由於試圖在 CLI 中傳送 header 引起的 ‘headers sent’ 錯誤。

CakeResponse API

class CakeResponse

CakeResponse 提供了一些與你傳送給客戶端的響應交互的有用的方法。

CakeResponse::header($header = null$value = null)

允許你直接設置一個或多個與響應同時傳送的 header。

CakeResponse::charset($charset = null)

設置在響應中使用的字符集。

CakeResponse::type($contentType = null)

設置響應的內容類型。你可以使用已知類型的別名或者類型的命名。

CakeResponse::cache($since$time = '+1 day')

允許你在響應中設置 cache header。

CakeResponse::disableCache()

設置這個 header 將禁止客戶端緩存響應。

CakeResponse::sharable($public = null$time = null)

將 Cache-Control header 設置成 public 或者 private ,選擇性的設置資源的 max-age 指令。

2.1 新版功能.

CakeResponse::expires($time = null)

允許你將 Expires header 設置爲確切日期。

2.1 新版功能.

CakeResponse::etag($tag = null$weak = false)

爲響應資源設置唯一的 Etag 標識。

2.1 新版功能.

CakeResponse::modified($time = null)

以正確的格式將 Last-Modified header 設置爲一個確切日期。

2.1 新版功能.

CakeResponse::checkNotModified(CakeRequest $request)

比較請求對象的 cache header 和 響應的 cache header,確定其是否仍然有效。如果仍然有效,則刪除所有響應內容,並傳送 304 Not Modified header。

2.1 新版功能.

CakeResponse::compress()

爲請求撕開 gzip 壓縮。

CakeResponse::download($filename)

允許你以附件的形式傳送響應,並設置文件名。

CakeResponse::statusCode($code = null)

允許你設置響應的狀態碼。

CakeResponse::body($content = null)

爲響應設置內容主體。

CakeResponse::send()

一旦你建立了響應,調用 send() 將傳送全部 heaer 和主體內容。在每個請求中,這是通過 Dispatcher 自動完成的。

CakeResponse::file($path$options = array())

允許你爲顯示或下載設置一個文件。

2.3 新版功能.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章