Yii 的修行之路 - HTTP的請求與響應

簡述

HTTP交互主要分爲 request(請求) 和 response(響應) 兩種方式。php

  • 對於Yii2來講,HTTP的request請求是用 YII::$app->request 來表示的對象,這是Yii提供的處理HTTP的request請求的插件,裏面有不少實用的功能代碼:git

<?php
        $request = \YII::$app->request;
        if ($request->isGet) {
            echo $request->get('id', 'get獲取的值爲空');
        } else if ($request->isPost) {
            $request->post('name', 'post傳遞的值爲空');
        }   
        echo $request->userIp; // 獲取當前IP地址
?>
  • 對於Yii2來講,HTTP的response請求是用 YII::$app->response 來表示的對象,這是Yii提供的處理HTTP的response請求的插件,裏面與不少實用的功能代碼:github

<?php
        $response = \YII::$app->response;
        // 設置服務器返回的狀態碼設置
        $response->statusCode = '404';
        // 設置pragma的設置,提醒瀏覽器不設置緩存
        $response->headers->add('pragma', 'no-chche');
        // 設置pragma的設置,提醒瀏覽器設置緩存5秒
        $response->headers->set('pragma', 'max-age=5');
        $response->headers->remove('pragma');
        // 設置頁面跳轉到指定路徑
        $response->headers->add('location', 'https://rongx.github.io');
        // 設置頁面跳轉,狀態碼設置爲302(調用Controller裏面的方法)
        $this->redirect('https://rongx.github.io', 302);
        // 設置文件下載連接
        $response->headers->add('content-dispostion', 'attachment; filename="a.jpg"');
        // 提供文件下載,文件路徑實相對與入口文件來講的
        $response->sendFile('./b.jpg');
?>
  • 其實在 yiiwebController 裏面也有不少處理HTTP的小插件功能代碼。web

請求參數(request)

要獲取請求參數,你能夠調用 request 組件的 yiiwebRequest::get() 方法和 yiiwebRequest::post() 方法。他們分別返回 $_GET 和$_POST 的值。數組

$request = Yii::$app->request;

$get = $request->get(); 
// 等價於: $get = $_GET;

$id = $request->get('id');   
// 等價於: $id = isset($_GET['id']) ? $_GET['id'] : null;

$id = $request->get('id', 1);   
// 等價於: $id = isset($_GET['id']) ? $_GET['id'] : 1;

$post = $request->post(); 
// 等價於: $post = $_POST;

$name = $request->post('name');   
// 等價於: $name = isset($_POST['name']) ? $_POST['name'] : null;

$name = $request->post('name', '');   
// 等價於: $name = isset($_POST['name']) ? $_POST['name'] : '';
  • 信息:建議你像上面那樣經過 request 組件來獲取請求參數,而不是 直接訪問 $_GET 和 $_POST。瀏覽器

  • 信息:這使你更容易編寫測試用例,由於你能夠僞造數據來建立一個模擬請求組件。緩存

當實現 RESTful APIs 接口的時候,你常常須要獲取經過PUT, PATCH或者其餘的 request methods 請求方法提交上來的參數。服務器

你能夠經過調用 yiiwebRequest::getBodyParam() 方法來獲取這些參數。例如:併發

$request = Yii::$app->request; // 返回全部參數 
$params = $request->bodyParams; // 返回參數 "id"
$param = $request->getBodyParam('id');
  • 信息:不一樣於 GET 參數,POST,PUT,PATCH 等等這些提交上來的參數是在請求體中被髮送的。app

  • 信息:當你經過上面介紹的方法訪問這些參數的時候,request 組件會解析這些參數。

  • 信息:你能夠經過配置 yiiwebRequest::parsers 屬性來自定義怎樣解析這些參數。

請求方法(request)

你能夠經過 request 表達式來獲取當前請求使用的HTTP方法。

這裏還提供了一整套布爾屬性用於檢測當前請求是某種類型。例如:

$request = Yii::$app->request;

if ($request->isAjax) { /* 該請求是一個 AJAX 請求 */ }
if ($request->isGet)  { /* 請求方法是 GET */ }
if ($request->isPost) { /* 請求方法是 POST */ }
if ($request->isPut)  { /* 請求方法是 PUT */ }

請求URLs(request)

request 組件提供了許多方式來檢測當前請求的URL。

假設被請求的URL是 http://example.com/admin/index.php/product?id=100, 你能夠像下面描述的那樣獲取URL的各個部分:

  • yiiwebRequest::url:返回 /admin/index.php/product?id=100, 此URL不包括host info部分。

  • yiiwebRequest::absoluteUrl:返回 http://example.com/admin/index.php/product?id=100, 包含host infode的整個URL。

  • yiiwebRequest::hostInfo:返回 http://example.com, 只有host info部分。

  • yiiwebRequest::pathInfo:返回 /product, 這個是入口腳本以後,問號以前(查詢字符串)的部分。

  • yiiwebRequest::queryString:返回 id=100,問號以後的部分。

  • yiiwebRequest::baseUrl:返回 /admin, host info以後, 入口腳本以前的部分。

  • yiiwebRequest::scriptUrl:返回 /admin/index.php, 沒有path info和查詢字符串部分。

  • yiiwebRequest::serverName:返回 example.com, URL中的host name。

  • yiiwebRequest::serverPort:返回 80, 這是web服務中使用的端口。

HTTP頭(request)

你能夠經過 yiiwebRequest::headers 屬性返回的 yiiwebHeaderCollection 獲取HTTP頭信息。 例如:

// $headers 是一個 yii\web\HeaderCollection 對象
$headers = Yii::$app->request->headers;

// 返回 Accept header 值
$accept = $headers->get('Accept');

if ($headers->has('User-Agent')) { /* 這是一個 User-Agent 頭 */ }

請求組件也提供了支持快速訪問經常使用頭的方法,包括:

  • yiiwebRequest::userAgent:返回 User-Agent 頭。

  • yiiwebRequest::contentType:返回 Content-Type 頭的值, Content-Type 是請求體中MIME類型數據。

  • yiiwebRequest::acceptableContentTypes:返回用戶可接受的內容MIME類型。 返回的類型是按照他們的質量得分來排序的。得分最高的類型將被最早返回。

  • yiiwebRequest::acceptableLanguages:返回用戶可接受的語言。 返回的語言是按照他們的偏好層次來排序的。第一個參數表明最優先的語言。

假如你的應用支持多語言,而且你想在終端用戶最喜歡的語言中顯示頁面,那麼你可使用語言協商方法 yiiwebRequest::getPreferredLanguage()。

這個方法經過 yiiwebRequest::acceptableLanguages 在你的應用中所支持的語言列表裏進行比較篩選,返回最適合的語言。

  • 提示:你也可使用 yiifiltersContentNegotiator 過濾器進行動態肯定哪些內容類型和語言應該在響應中使用。
    這個過濾器實現了上面介紹的內容協商的屬性和方法。

客戶端信息(request)

你能夠經過 yiiwebRequest::userHost 和 yiiwebRequest::userIP 分別獲取host name和客戶機的IP地址, 例如,

$userHost = Yii::$app->request->userHost;
$userIP = Yii::$app->request->userIP;

響應(response)

當應用完成處理一個請求後, 會生成一個yiiwebResponse響應對象併發送給終端用戶 響應對象包含的信息有HTTP狀態碼,HTTP頭和主體內容等。

網頁應用開發的最終目的本質上就是根據不一樣的請求構建這些響應對象。

在大可能是狀況下主要處理繼承自 yiiwebResponse 的 response 應用組件, 儘管如此,Yii也容許你建立你本身的響應對象併發送給終端用戶。

狀態碼

構建響應時,最早應作的是標識請求是否成功處理的狀態,可經過設置 yiiwebResponse::statusCode 屬性,該屬性使用一個有效的HTTP 狀態碼。

例如,爲標識處理已被處理成功, 可設置狀態碼爲200,以下所示:

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

儘管如此,大多數狀況下不須要明確設置狀態碼,由於 yiiwebResponse::statusCode 狀態碼默認爲200, 若是須要指定請求失敗,可拋出對應的HTTP異常,以下所示:

throw new \yii\web\NotFoundHttpException;

當錯誤處理器 捕獲到一個異常,會從異常中提取狀態碼並賦值到響應, 對於上述的 yiiwebNotFoundHttpException 對應HTTP 404狀態碼,如下爲Yii預約義的HTTP異常:

  • yiiwebBadRequestHttpException: status code 400.

  • yiiwebConflictHttpException: status code 409.

  • yiiwebForbiddenHttpException: status code 403.

  • yiiwebGoneHttpException: status code 410.

  • yiiwebMethodNotAllowedHttpException: status code 405.

  • yiiwebNotAcceptableHttpException: status code 406.

  • yiiwebNotFoundHttpException: status code 404.

  • yiiwebServerErrorHttpException: status code 500.

  • yiiwebTooManyRequestsHttpException: status code 429.

  • yiiwebUnauthorizedHttpException: status code 401.

  • yiiwebUnsupportedMediaTypeHttpException: status code 415.

若是想拋出的異常不在如上列表中,可建立一個yiiwebHttpException異常,帶上狀態碼拋出,以下:

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

HTTP 頭部

可在 response 組件中操控yiiwebResponse::headers來發送HTTP頭部信息,例如:

$headers = Yii::$app->response->headers;

// 增長一個 Pragma 頭,已存在的Pragma 頭不會被覆蓋。
$headers->add('Pragma', 'no-cache');

// 設置一個Pragma 頭. 任何已存在的Pragma 頭都會被丟棄
$headers->set('Pragma', 'no-cache');

// 刪除Pragma 頭並返回刪除的Pragma 頭的值到數組
$values = $headers->remove('Pragma');
  • 補充: 頭名稱是大小寫敏感的,在yiiwebResponse::send()方法調用前新註冊的頭信息並不會發送給用戶。

響應主體

大可能是響應應有一個主體存放你想要顯示給終端用戶的內容。

若是已有格式化好的主體字符串,可賦值到響應的yiiwebResponse::content屬性,例如:

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

若是在發送給終端用戶以前須要格式化,應設置 yiiwebResponse::format 和 yiiwebResponse::data 屬性。

yiiwebResponse::format 屬性指定 yiiwebResponse::data 中數據格式化後的樣式,例如:

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

Yii支持如下可直接使用的格式,每一個實現了 yiiwebResponseFormatterInterface 類, 可自定義這些格式器或經過配置 yiiwebResponse::formatters 屬性來增長格式器。

  • yiiwebResponse::FORMAT_HTML: 經過 yiiwebHtmlResponseFormatter 來實現.

  • yiiwebResponse::FORMAT_XML: 經過 yiiwebXmlResponseFormatter來實現.

  • yiiwebResponse::FORMAT_JSON: 經過 yiiwebJsonResponseFormatter來實現.

  • yiiwebResponse::FORMAT_JSONP: 經過 yiiwebJsonResponseFormatter來實現.

上述響應主體可明確地被設置,可是在大多數狀況下是經過 操做 方法的返回值隱式地設置,經常使用場景以下所示:

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

上述的 index 操做返回 index 視圖渲染結果,返回值會被 response 組件格式化後發送給終端用戶。

由於響應格式默認爲 yiiwebResponse::FORMAT_HTML, 只須要在操做方法中返回一個字符串, 若是想使用其餘響應格式,應在返回數據前先設置格式,例如:

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

如上所述,觸雷使用默認的 response 應用組件,也可建立本身的響應對象併發送給終端用戶,可在操做方法中返回該響應對象,以下所示:

public function actionInfo(){ 
    return \Yii::createObject([ 
        'class' => 'yii\web\Response', 
        'format' => \yii\web\Response::FORMAT_JSON, 
        'data' => [ 'message' => 'hello world', 'code' => 100, ], 
    ]); 
}
  • 注意: 若是建立你本身的響應對象,將不能在應用配置中設置 response 組件,儘管如此, 可以使用 依賴注入應用通用配置到你新的響應對象。

瀏覽器跳轉

瀏覽器跳轉依賴於發送一個Location HTTP 頭,由於該功能一般被使用,Yii提供對它提供了特別的支持。

可調用 yiiwebResponse::redirect() 方法將用戶瀏覽器跳轉到一個URL地址。

該方法設置合適的 帶指定URL的 Location 頭並返回它本身爲響應對象,在操做的方法中,可調用縮寫版 yiiwebController::redirect(),例如:

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

在如上代碼中,操做的方法返回redirect() 方法的結果,如前所述,操做的方法返回的響應對象會被當總響應發送給終端用戶。

除了操做方法外,可直接調用 yiiwebResponse::redirect() 再調用 yiiwebResponse::send() 方法來確保沒有其餘內容追加到響應中。

\Yii::$app->response->redirect('http://example.com/new', 301)->send();
  • 補充: yiiwebResponse::redirect() 方法默認會設置響應狀態碼爲302,該狀態碼會告訴瀏覽器請求的資源 臨時 放在另外一個URI地址上,可傳遞一個301狀態碼告知瀏覽器請求的資源已經 永久 重定向到新的URId地址。

若是當前請求爲AJAX 請求,發送一個 Location 頭不會自動使瀏覽器跳轉,爲解決這個問題, yiiwebResponse::redirect() 方法設置一個值爲要跳轉的URL的X-Redirect 頭, 在客戶端可編寫JavaScript 代碼讀取該頭部值而後讓瀏覽器跳轉對應的URL。

  • 補充: Yii 配備了一個yii.js JavaScript 文件提供經常使用JavaScript功能,包括基於X-Redirect頭的瀏覽器跳轉, 所以,若是你使用該JavaScript 文件( 經過yiiwebYiiAsset 資源包註冊),就不須要編寫AJAX跳轉的代碼。

發送文件

和瀏覽器跳轉相似,文件發送是另外一個依賴指定HTTP頭的功能,Yii提供方法集合來支持各類文件發送需求,它們對HTTP頭都有內置的支持。

  • yiiwebResponse::sendFile(): 發送一個已存在的文件到客戶端

  • yiiwebResponse::sendContentAsFile(): 發送一個文本字符串做爲文件到客戶端

  • yiiwebResponse::sendStreamAsFile(): 發送一個已存在的文件流做爲文件到客戶端

這些方法都將響應對象做爲返回值,若是要發送的文件很是大,應考慮使用 yiiwebResponse::sendStreamAsFile() 由於它更節約內存,如下示例顯示在控制器操做中如何發送文件:

public function actionDownload(){ 
    return \Yii::$app->response->sendFile('path/to/file.txt'); 
}

若是不是在操做方法中調用文件發送方法,在後面還應調用 yiiwebResponse::send() 沒有其餘內容追加到響應中。

\Yii::$app->response->sendFile('path/to/file.txt')->send();

一些瀏覽器提供特殊的名爲X-Sendfile的文件發送功能,原理爲將請求跳轉到服務器上的文件, Web應用可在服務器發送文件前結束,爲使用該功能,可調用yiiwebResponse::xSendFile(), 以下簡要列出一些經常使用Web服務器如何啓用X-Sendfile 功能:

  • Apache: X-Sendfile

  • Lighttpd v1.4: X-LIGHTTPD-send-file

  • Lighttpd v1.5: X-Sendfile

  • Nginx: X-Accel-Redirect

  • Cherokee: X-Sendfile and X-Accel-Redirect

發送響應

在 send() 方法調用前響應中的內容不會發送給用戶,該方法默認在yiibaseApplication::run() 結尾自動調用,儘管如此,能夠明確調用該方法強制當即發送響應。

yiiwebResponse::send() 方法使用如下步驟來發送響應:

  1. 觸發 yiiwebResponse::EVENT_BEFORE_SEND 事件.

  2. 調用 yiiwebResponse::prepare() 來格式化 yiiwebResponse::data 爲yiiwebResponse::content.

  3. 觸發 yiiwebResponse::EVENT_AFTER_PREPARE 事件.

  4. 調用 yiiwebResponse::sendHeaders() 來發送註冊的HTTP頭

  5. 調用 yiiwebResponse::sendContent() 來發送響應主體內容

  6. 觸發 yiiwebResponse::EVENT_AFTER_SEND 事件.

一旦yiiwebResponse::send() 方法被執行後,其餘地方調用該方法會被忽略, 這意味着一旦響應發出後,就不能再追加其餘內容。

如你所見yiiwebResponse::send() 觸發了幾個實用的事件,經過響應這些事件可調整或包裝響應。

相關文章
相關標籤/搜索