Yii2.0 RESTful風格的Controller與ActiveController

AlanJager發表於2020-04-07

yii\rest\Controller提供的大多數RESTful API功能通過過濾器實現. 特別是以下過濾器會按順序執行:

  • yii\filters\ContentNegotiator: 支援內容協商。
  • yii\filters\VerbFilter: 支援HTTP 方法驗證; 
  • yii\filters\AuthMethod: 支援使用者認證;
  • yii\filters\RateLimiter: 支援頻率限制.

這些過濾器都在yii\rest\Controller::behaviors()方法中宣告。原本的宣告如下:

    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        return [
            'contentNegotiator' => [
                'class' => ContentNegotiator::className(),
                'formats' => [
                    'application/json' => Response::FORMAT_JSON,
                    'application/xml' => Response::FORMAT_XML,
                ],
            ],
            'verbFilter' => [
                'class' => VerbFilter::className(),
                'actions' => $this->verbs(),
            ],
            'authenticator' => [
                'class' => CompositeAuth::className(),
            ],
            'rateLimiter' => [
                'class' => RateLimiter::className(),
            ],
        ];
    }
首先是yii\filters\ContentNegotiator過濾器,該過濾器是用於內容協商的,例如, 如果一個 RESTful API 請求中包含以下 header,
Accept: application/json; q=1.0, */*; q=0.1

將會得到JSON格式的響應,幕後,執行一個 RESTful API 控制器動作之前,yii\filters\ContentNegotiator filter 將檢查 HTTP header 在請求時和配置 yii\web\Response::format 為 json。 之後的動作被執行並返回得到的資源物件或集合, yii\rest\Serializer 將結果轉換成一個陣列。最後,yii\web\JsonResponseFormatter 該陣列將序列化為JSON字串,並將其包括在響應主體。

之後將會執行yii\filters\VerbFilter,這一步是對動詞的過濾,參考yii\rest\ActiveController中的宣告:

    /**
     * @inheritdoc
     */
    protected function verbs()
    {
        return [
            'index' => ['GET', 'HEAD'],
            'view' => ['GET', 'HEAD'],
            'create' => ['POST'],
            'update' => ['PUT', 'PATCH'],
            'delete' => ['DELETE'],
        ];
    }

所謂的動詞即是使用者發出的請求的型別,由此對應不同的操作。

然後是yii\filters\AuthMethod和Web應用不同,RESTful APIs 通常是無狀態的,也就意味著不應使用sessions 或 cookies, 因此每個請求應附帶某種授權憑證,因為使用者授權狀態可能沒通過sessions 或 cookies維護, 常用的做法是每個請求都傳送一個祕密的access token來認證使用者,由於access token可以唯一識別和認證使用者。

最後是yii\filters\RateLimiter為防止濫用,你應該考慮增加速率限制到您的API。 例如,您可以限制每個使用者的API的使用是在10分鐘內最多100次的API呼叫。 如果一個使用者同一個時間段內太多的請求被接收, 將返回響應狀態程式碼 429 (這意味著過多的請求)。

要啟用速率限制, yii\web\User::identityClass 應該實現 yii\filters\RateLimitInterface. 這個介面需要實現以下三個方法:

  • getRateLimit(): 返回允許的請求的最大數目及時間,例如,[100, 600] 表示在600秒內最多100次的API呼叫。
  • loadAllowance(): 返回剩餘的允許的請求和相應的UNIX時間戳數 當最後一次速率限制檢查時。
  • saveAllowance(): 儲存允許剩餘的請求數和當前的UNIX時間戳。

你可以在user表中使用兩列來記錄容差和時間戳資訊。 loadAllowance() 和 saveAllowance() 可以通過實現對符合當前身份驗證的使用者 的這兩列值的讀和儲存。為了提高效能,你也可以 考慮使用快取或NoSQL儲存這些資訊。

一旦 identity 實現所需的介面, Yii 會自動使用 yii\filters\RateLimiter 為 yii\rest\Controller 配置一個行為過濾器來執行速率限制檢查。 如果速度超出限制 該速率限制器將丟擲一個 yii\web\TooManyRequestsHttpException。 你可以在你的 REST 控制器類裡配置速率限制,

public function behaviors()
{
    $behaviors = parent::behaviors();
    $behaviors['rateLimiter']['enableRateLimitHeaders'] = false;
    return $behaviors;
}

當速率限制被啟用,預設情況下每個響應將包含以下HTTP頭髮送 目前的速率限制資訊:

  • X-Rate-Limit-Limit: 同一個時間段所允許的請求的最大數目;
  • X-Rate-Limit-Remaining: 在當前時間段內剩餘的請求的數量;
  • X-Rate-Limit-Reset: 為了得到最大請求數所等待的秒數。

你可以禁用這些頭資訊通過配置 yii\filters\RateLimiter::enableRateLimitHeaders 為false, 就像在上面的程式碼示例所示。

yii\rest\ActiveController繼承了yii\rest\Controller,在這個基礎上,封裝了基本的RESTful API:
    /**
     * @inheritdoc
     */
    public function actions()
    {
        return [
            'index' => [
                'class' => 'yii\rest\IndexAction',
                'modelClass' => $this->modelClass,
                'checkAccess' => [$this, 'checkAccess'],
            ],
            'view' => [
                'class' => 'yii\rest\ViewAction',
                'modelClass' => $this->modelClass,
                'checkAccess' => [$this, 'checkAccess'],
            ],
            'create' => [
                'class' => 'yii\rest\CreateAction',
                'modelClass' => $this->modelClass,
                'checkAccess' => [$this, 'checkAccess'],
                'scenario' => $this->createScenario,
            ],
            'update' => [
                'class' => 'yii\rest\UpdateAction',
                'modelClass' => $this->modelClass,
                'checkAccess' => [$this, 'checkAccess'],
                'scenario' => $this->updateScenario,
            ],
            'delete' => [
                'class' => 'yii\rest\DeleteAction',
                'modelClass' => $this->modelClass,
                'checkAccess' => [$this, 'checkAccess'],
            ],
            'options' => [
                'class' => 'yii\rest\OptionsAction',
            ],
        ];
    }

在建立自己的RESTful API時可以通過對yii\rest\ActiveController::actions()的過載來對對應的方法進行禁用和允許的設定。同時要對yii\rest\ActiveController::verbs()進行過載:

    /**
     * @inheritdoc
     */
    protected function verbs()
    {
        return [
            'index' => ['GET', 'HEAD'],
            'view' => ['GET', 'HEAD'],
            'create' => ['POST'],
            'update' => ['PUT', 'PATCH'],
            'delete' => ['DELETE'],
        ];
    }

用於處理不同的動作對應的HTTP請求方式。

最後yii\rest\ActiveController提供了一個yii\rest\ActiveController::checkAccess()方法。該方法用於檢驗當前發起請求的使用者是否有許可權使用某資料層進行某個操作。




相關文章