【Yii系列】處理請求

緣起

這一章是Yii系列的第三章,前兩章給大夥講解了Yii2.0的安裝與Yii2.0的基本框架及基礎概念,傳送門:php

【Yii2.0的安裝與調試】:http://www.cnblogs.com/riverdubu/p/6439680.htmlhtml

【Yii2.0基礎框架】:http://www.cnblogs.com/riverdubu/p/6607373.html前端

相信學習過上兩章的內容,大家是否是對Yii有個大體的瞭解了呢,Yii2.0基礎框架這一章很重要,不只僅是由於它的長度,而是裏面講解了整個Yii2.0的基礎概念,以及如何基於MVC模型構建的它的整套框架,只是浮於表面的一些基礎只是,若是你們對更深層次的Yii的底層感興趣,能夠持續關注個人博客,後面我會對於Yii底層的一些關鍵組件和代碼作更加詳細的講解。react

接上一章的話,咱們這章帶大夥瞭解下一個用戶的請求從發起到收到響應的整個過程是如何實現,這對於統籌全局相當重要,若是說上一章是基礎,是打根基和搭架子的一章,那麼這一章就是給你將整個房子建起來啦,會把全部的門窗房間規劃好,讓你可以輕鬆自如的在房子裏面穿梭。git

運行機制

首先,咱們來看下整個請求的運行機制web

用戶發送請求給入口腳本,入口腳本加載配置,運行application,application會建立一個request組件去處理此次用戶請求,Request組件會去路由裏面查找用戶想要請求的那個Controller,找到Controller後實例化它,調用對應的action執行操做,action會調用對應model層的函數進行數據處理,處理完成以後返回給對應的action,action會將數據格式化或者不格式化渲染View,爲其提供填充所須要的數據,渲染完成的結果會返回給response組件發送給用戶瀏覽器。json

這裏面有幾個關鍵的組件,request和response咱們先不用去管他們,這是application會自動搞定的,咱們要關心關心這個被稱爲路由的東西。後端

路由

有幾個關鍵概念是須要你們理解下的,引導路由。api

當入口腳本在調用 yii\web\Application::run() 方法時,它進行的第一個操做就是解析輸入的請求,而後實例化對應的Controller處理這個請求。 該過程就被稱爲引導路由(routing)數組

咱們以前可能看過相似於下面的請求URL

http://服務器IP/index.php?r=post/view&id=100

這邊的r後面的就是後面會被實例化的Controller去處理此次請求,這是標準的寫法,可是,爲了讓URL看上去更pretty一些,比方說下面的URL

http://服務器IP/post/view?id=100

這樣是否是可以更清晰的看出是哪一個業務單元的邏輯,這樣的寫法須要經過配置應用主體Components中一個叫urlManager的配置項來完成路由的解析。

這兩種寫法之間經過配置urlManager裏面的enablePrettyUrl屬性來實現切換,true的話是下面一種寫法,false的話是上面一種寫法。

引導路由包含兩步,第一步,請求會被解析成對應的路由和請求參數,第二步,一個路由對應的Controller的action會被實例化去處理這個請求。

當你使用pretty方式去解析你的URL時,urlManager會在當前的規則中尋找你想要找的規則,若是找不到,會拋出一個 yii\web\NotFoundHttpException,這就是大名鼎鼎的404啦。

一旦找到對應的路由規則,URL會被拆分紅不少部分,就像上面的post/view同樣,從前日後可能分別對應了一個module,一個controller,一個action。application回一個個去嘗試的,若是沒有找到對應的action去執行這個請求,仍是會像上面同樣,拋出大名鼎鼎的404。

咱們這邊對基礎的路由不作解析,想要了解的朋友能夠去官網詳細的查看下。咱們來具體的來看下pretty的路由規則。

爲了可以使用pretty路由解析,咱們得在應用主體,也就是上一章的web.php中配置一下。

複製代碼
[
    'components' => [
        'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'enableStrictParsing' => false,
            'rules' => [
                // ...
            ],
        ],
    ],
]
複製代碼

具體的規則就要配置在rules,這裏建議一下,這個rules配置在一個文件中,而後這邊require一下便可。

這裏面有幾個參數

enablePrettyUrl:和上面的意思同樣,打開pretty url規則的,這個必須得打開,要否則的話默認的路由解析會是基礎路由解析。

showScriptName:是否將入口腳本寫入用戶請求的連接,若是爲true,請求連接爲:/index.php/post/100,若是爲false,請求連接爲:/post/100

enableStrictParsing:是否須要按照rules裏的規則嚴格解析,若是爲true,請求的連接必須符合rules裏面的規則,若是不知足,拋出404,若是爲false,請求連接能夠是rules規則的一部分。

url rules

下面咱們就來詳細的講一講須要配置的這個rules文件。

咱們先來看個例子

[
    'posts' => 'post/index', 
    'post/<id:\d+>' => 'post/view',
]

這邊定義了兩條URL rules

第一條將URL中的posts對應到post/index這條路由;

第二條規則匹配一個正則式post/(\d+)對應到post/view這條路由,id是它的參數。

當URL被規則解析,他後面的參數會被收錄進application的request組件,後面咱們會說到這個request組件。在我看來,處女座的人比較滿意的一個url是這樣的:http://example.com/psot/action?a=1&b=2,這是比較常規的

我這邊給出的一個例子適用於大多數狀況

'v1/<module:\w+>/<controller:\w+>/<action:\w+>' => 'api/v1/<module>/<controller>/<action>', //模塊相關規則

前面部分是用戶訪問時的連接格式,後面部分是項目代碼中對應的action位置。

經過正則式去解析路由,這回大大減小規則數量,大大提高urlManager的性能。

Request 

講完了路由,咱們再來看下一個請求是如何被應用主體接受和反饋的吧。

首先,咱們來看下在應用中,用戶的請求是被實例化成啥的。

一個應用的請求是用 yii\web\Request 對象來表示的,該對象提供了諸如 請求參數(譯者注:一般是GET參數或者POST參數)、HTTP頭、cookies等信息。

調用方式

Yii::$app->request

$request->get()這段代碼等價於$_GET,以前和大夥說過,不能直接使用$_GET這個值,這使你更容易編寫測試用例,由於你能夠僞造數據來建立一個模擬請求組件。

你能夠經過 Yii::$app->request->method 表達式來獲取當前請求使用的HTTP方法。

複製代碼
$request = Yii::$app->request;

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

我這邊通常的是使用一個Web基類去獲取請求的body和head

$requestBody = $this->getParam('body');
$requestHeader = $this->getParam('header');

Web基類

複製代碼
<?php
namespace ext\controller;

use Yii;
use yii\web\Controller;

class Web extends Controller
{
    public function init()
    {
        parent::init();
        $this->setDefaultCrumbs();
    }

    public function beforeaction($action)
    {
        return parent::beforeaction($action);
    }

    ......

    /**
     * 獲取參數
     * @param null $key
     * @param null $val
     * @return array|mixed|null
     */
    public function getParam($key = null, $val = null)
    {

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

        $data = [];
        /**
         * 若是是GET請求
         */
        if ($request->getIsGet()) {
            $data = $request->get('request');
        } elseif ($request->getIsPost()) {
            if($request->contentType == 'application/json')
            {
                $rawBody = $request->rawBody;
                if($rawBody){
                    $requestData = json_decode($rawBody,true);
                    $data = $requestData['request'];
                    unset($requestData);
                }
            }else{
                $data = $request->post('request');
            }
        } elseif ($request->getIsDelete()) {
            $data = $request->get('request');
        }

        if (isset($data[$key])) {
            if (is_null($data[$key])) {
                return $val;
            } else {
                return $data[$key];
            }
        } elseif (is_null($key)) {
            return $data;
        } else {
            return $val;
        }
    }
}
複製代碼

詳細的代碼待我完成Yii系列最佳時間貢獻到githun供大夥下載研究呢。

Response

看完了請求的引導,咱們來看下咱們如何反饋給用戶吧。

Yii是經過response這個組件來反饋給用戶咱們想要給到他的信息的。

Response對象包含的信息有HTTP狀態碼,HTTP頭和主體內容等, 網頁應用開發的最終目的本質上就是根據不一樣的請求構建這些響應對象。

響應中首當其衝的應該就是狀態碼了,也就是咱們以前提到的404啊,503啊,301啊,這些都是服務器的狀態碼。

Yii提供了一些常量去定義這些狀態碼

複製代碼
yii\web\BadRequestHttpException:狀態碼 400。
yii\web\ConflictHttpException:狀態碼 409。
yii\web\ForbiddenHttpException:狀態碼 403。
yii\web\GoneHttpException:狀態碼 410。
yii\web\MethodNotAllowedHttpException:狀態碼 405。
yii\web\NotAcceptableHttpException:狀態碼 406。
yii\web\NotFoundHttpException:狀態碼 404。
yii\web\ServerErrorHttpException:狀態碼 500。
yii\web\TooManyRequestsHttpException:狀態碼 429。
yii\web\UnauthorizedHttpException:狀態碼 401。
yii\web\UnsupportedMediaTypeHttpException:狀態碼 415。
複製代碼

yii\web\Response::statusCode 狀態碼默認爲200, 若是須要指定請求失敗,可拋出對應的上訴的HTTP異常。

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

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

可在 response 組件中操控yii\web\Response::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');
複製代碼

響應主體

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

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

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

Yii可使用以下的幾種格式

HTML: 經過 yii\web\HtmlResponseFormatter 來實現.
XML: 經過 yii\web\XmlResponseFormatter來實現.
JSON: 經過 yii\web\JsonResponseFormatter來實現.
JSONP: 經過 yii\web\JsonResponseFormatter來實現.
RAW: use this format if you want to send the response directly without applying any formatting.

通常的話,咱們不會這麼麻煩的去操做,而是直接和前端商量好,咱們會以哪一種形式返回給你,而後直接在action裏面直接返回數據給用戶。

Web基類中對應的方法:

複製代碼
public function sendSuccess($message = null, $data = [], $code = "0")
    {
        return $this->formatJson([
                'response' => [
                    'header' => [
                        'code' => $code,
                        'msg' => $message
                    ],
                    'body' => $data ?: []
                ]
            ]
        );
    }

    public function sendError($message = null, $code = "1")
    {
        return $this->formatJson([
                'response' => [
                    'header' => [
                        'code' => (string)$code,
                        'msg' => $message ? $message : '未知錯誤'
                    ],
                ]
            ]
        );
    }
複製代碼

session

除了Request和Response這兩個基本概念,還有個概念比較重要,若是你想讓你用戶的數據在你後端構成一個map,你可使用session這個組件,相信我,這個組件絕對可以知足大部分用戶登陸系統,用他來保存用戶的數據很是方便,每次用戶來請求,只須要將sessionId傳給你,你就能夠獲取到這個用戶的狀態,雖然方便,但不能徹底保證安全,若是sessionId被截取了,這也是至關蛋疼的事。這就要看系統的健壯性了,用戶的敏感信息是否會在前端有權限拿到,若是要拿敏感信息,你的系統又當如何。

這裏很少說,會在安全那一章和大夥好好嘮嘮後臺應用中提升安全係數的一些作法呢。

下面直接上session相關的代碼,session的操做也很簡單,key-value模型。

複製代碼
$session = Yii::$app->session;

// 檢查session是否開啓 
if ($session->isActive) ...

// 開啓session
$session->open();

// 關閉session
$session->close();

// 銷燬session中全部已註冊的數據
$session->destroy();
複製代碼

比較重要的一個方法是設置session存在的時間:

$session->setTimeout(...)

session數據的存儲和提取

複製代碼
$session = Yii::$app->session;

// 獲取session中的變量值,如下用法是相同的:
$language = $session->get('language');
$language = $session['language'];
$language = isset($_SESSION['language']) ? $_SESSION['language'] : null;

// 設置一個session變量,如下用法是相同的:
$session->set('language', 'en-US');
$session['language'] = 'en-US';
$_SESSION['language'] = 'en-US';

// 刪除一個session變量,如下用法是相同的:
$session->remove('language');
unset($session['language']);
unset($_SESSION['language']);

// 檢查session變量是否已存在,如下用法是相同的:
if ($session->has('language')) ...
if (isset($session['language'])) ...
if (isset($_SESSION['language'])) ...

// 遍歷全部session變量,如下用法是相同的:
foreach ($session as $name => $value) ...
foreach ($_SESSION as $name => $value) ...
複製代碼

Flash數據是一種特別的session數據,它一旦在某個請求中設置後, 只會在下次請求中有效,而後該數據就會自動被刪除。喜歡的朋友能夠去官方文當中詳查。

關於cookie,後端使用的較少,前端用的時候常常用它來存儲一些數據。這裏很少說,感興趣的朋友能夠去官方文當中詳查。

至此,全部的關於用戶的請求Yii是如何處理的就講完啦,包括從URL解析,Request和Response組件,Session組件這一整套內容,其實,官網將錯誤處理,日誌系統歸類到這章,我感受這兩章對於開發者來說仍是比較重要的,因此,我下面會分兩個小章來詳細講講這兩個系統。^_^

相關文章
相關標籤/搜索