溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Yii2中Restful API的示例分析

發(fā)布時(shí)間:2021-08-30 14:53:02 來(lái)源:億速云 閱讀:112 作者:小新 欄目:開(kāi)發(fā)技術(shù)

這篇文章將為大家詳細(xì)講解有關(guān)Yii2中Restful API的示例分析,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

具體如下:

Yii2 有個(gè)很重要的特性是對(duì) Restful API的默認(rèn)支持, 通過(guò)短短的幾個(gè)配置就可以實(shí)現(xiàn)簡(jiǎn)單的對(duì)現(xiàn)有Model的RESTful API

這里通過(guò)分析rest部分源碼,簡(jiǎn)單剖析下yii2 實(shí)現(xiàn) restful 的原理,并通過(guò)一些定制實(shí)現(xiàn) 對(duì) 關(guān)聯(lián)模型的RESTful api 操作。

~ 代表 extends from 的關(guān)系

| | rest/
| | |-Action.php ~ `\yii\base\Action`
| | |-Controller.php ~  `\yii\web\Controller`
| | | |-ActiveController.php ~ `rest\Controller`
| | |-Serializer.php ~ `yii\base\Component`
| | |-UrlRule.php ~ `yii\web\CompositeUrlRule`
| | |-CreateAction.php ~ `rest\Action`
| | |-DeleteAction.php ~ `rest\Action`
| | |-IndexAction.php ~ `rest\Action`
| | |-OptionsAction.php ~ `rest\Action`
| | |-UpdateAction.php ~ `rest\Action`
| | |-ViewAction.php ~ `rest\Action`

1. rest/Controller ~ \yii\web\Controller

Controller是 RESTful API 控制器類(lèi)的基類(lèi)

它在一個(gè)API請(qǐng)求的控制周期中一次實(shí)現(xiàn)了下面的步驟 1~5:

① 解析響應(yīng)的內(nèi)容格式
② 校驗(yàn)請(qǐng)求方法
③ 檢驗(yàn)用戶(hù)權(quán)限
④ 限制速度
⑤ 格式化響應(yīng)數(shù)據(jù)

use yii\filters\auth\CompositeAuth;
use yii\filters\ContentNegotiator;
use yii\filters\RateLimiter;
use yii\web\Response;
use yii\filters\VerbFilter;
/**
 * Controller is the base class for RESTful API controller classes.
 *
 * Controller implements the following steps in a RESTful API request handling cycle
 * 1. Resolving response format (see [[ContentNegotiator]]);
 * 2. Validating request method (see [[verbs()]]).
 * 3. Authenticating user (see [[\yii\filters\auth\AuthInterface]]);
 * 4. Rate limiting (see [[RateLimiter]]);
 * 5. Formatting response data (see [[serializeData()]])
behaviors
  contentNegotiator
  verbFilter
  authenticator
  rateLimiter
afterAction
  serializeData Yii::createObject($this->serializer)->serialize($data)
verbs []
*/
class Controller extends \yii\web\Controller
{
  public $serializer = 'yii\rest\Serializer';
  public $enableCsrfValidation = false;
  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(),
      ],
    ]
  }
  public function verbs()
  {
    return [];
  }
  public function serializeData($data)
  {
    return Yii::createObject($this->serializer)->serialize($data);
  }
  public function afterAction($action, $result)
  {
    $result = parent::afterAction($action, $result);
    return $this->serializeData($result);
  }
}

2. rest/ActiveController ~ rest/Controller

ActiveController 實(shí)現(xiàn)了一系列的和 ActiveRecord 互通數(shù)據(jù)的RESTful方法

ActiveRecord 的類(lèi)名由 modelClass 變量指明, yii\db\ActiveRecordInterface ???

默認(rèn)的, 支持下面的方法:

 * - `index`: list of models
 * - `view`: return the details of a model
 * - `create`: create a new model
 * - `update`: update an existing model
 * - `delete`: delete an existing model
 * - `options`: return the allowed HTTP methods

可以通過(guò)覆蓋 actions() 并且 unsetting 響應(yīng)的 action 來(lái)禁用這些默認(rèn)的動(dòng)作。

要增加一個(gè)新的動(dòng)作, 覆蓋 actions() 向其末尾增加一個(gè)新的 action class 或者 是一個(gè)新的 action method

注意一點(diǎn),確保你同時(shí)也覆蓋了 verbs() 方法來(lái)聲明這個(gè)新的動(dòng)作支持那些HTTP Method

也需要覆蓋checkAccess() 來(lái)檢查當(dāng)前用戶(hù)是否有權(quán)限來(lái)執(zhí)行響應(yīng)的某個(gè)動(dòng)作。

根據(jù)上面的說(shuō)明再寫(xiě)一遍 Controller

class ActiveController extends Controller
{
  public #modelClass;
  public $updateScenario = Model::SCENARIO_DEFAULT;
  public $createScenario = Model::SCENARIO_DEFAULT;
  public function init()
  {
    parent::init();
    if($this->modelClass == null){
      throw new InvalidConfigException('The "modelClass" property must be set.');
    }
  }
  public function actions()
  {
    return [
      'index' => [
        'class' => 'app\controllers\rest\IndexAction',
        'modelClass' => $this->modelClass,
        'checkAccess' => [$this, 'checkAccess'],
      ],
      'view'...
      'create'...
      'update'...
      'delete'...
      'options'...
    ];
  }
  protected function verbs()
  {
    return [
      'index' => ['GET', 'HEAD'],
      'view' =>['GET', 'HEAD'],
      'create' =>['POST'],
      'update' =>['PUT', 'PATCH'],
      'delete' =>['DELETE'],
    ];
  }
  public function checkAccess($action, $model=null, $params = [])
  {
  }
}

下面來(lái)實(shí)現(xiàn)一個(gè)繼承自 這個(gè)rest\ActiveController的 News 控制器:

namespace app\controllers;
use app\controllers\rest\ActiveController; #剛才這個(gè)AC,我從yii/rest下面拷貝了一份出來(lái)
class NewsController extends ActiveController
{
  public $modelClass ='app\models\News';
}

定義到這里就足夠?qū)崿F(xiàn) rest\ActiveController 里面的默認(rèn)方法了
下面來(lái)覆蓋下,實(shí)現(xiàn)一些定制的方法

class NewsController extends ActiveController
{
  public $modelClass = 'app\models\News';
  #定制serializer
  #public $serializer = 'yii\rest\Serializer';
  public $serializer = [
    'class' => 'app\controllers\rest\Serializer',
    'collectionEnvelope' => 'items',
  ];
  public function behaviors()
  {
    $be = ArrayHelper::merge(
      parent::behaviors(),
      [
        'verbFilter' => [
          'class' => VerbFilter::className(),
          'actions' => [
            'index' => ['get'],
            ...
          ]
        ],
        'authenticator' => [
          'class' => CompositeAuth::className(),
          'authMethods' => [
            HttpBasicAuth::className(),
            HttpBearerAuth::className(),
            QueryParamAuth::className(),
          ]
        ],
        'contentNegotiator' => [
          'class' => ContentNegotiator::className(),
          'formats' => [
            'text/html' => Response::FORMAT_HTML,
          ]
        ],
        'access' => [
          'class' => AccessControl::className(),
          'only' => ['view'],
          'rules' => [
            [
             'actions' => ['view'],
             'allow' => false,
             'roles' => ['@'],
            ],
         ],
        ]
      ],
    );
    return $be;
  }
  public function checkAccess()
  {
  }
}

3. 定制Actions

如果要對(duì) Actions 進(jìn)行大的改動(dòng),建議拷貝一份出來(lái),不要使用原始的 yii\rest\XXXAction命名空間

我這里以要實(shí)現(xiàn)對(duì)related models進(jìn)行 CURD 操作為目標(biāo)進(jìn)行大的改動(dòng)

Action

在定制各個(gè)action之前, 先看看它們的基類(lèi) rest\Action, 主要是一個(gè) findModel的方法

class Action extend \yii\base\Action
{
  public $modelClass;
  public $findModel;
  public $checkAccess;
  public function init()
  {
    if($this->modelClass == null) {
      throw new InvalidConfigException(get_class($this). '::$modelClass must be set');
    }
  }
  public function findModel($id)
  {
    if($this->findModel !== null) {
      return call_user_func($this->findModel, $id, $this);
    }
    $modelClass = $this->modelClass;
    $keys = $modelClass::primaryKey();
    if(count($keys) > 1) {
      $values = explode(',', $id);
      if..
    } elseif($id !== null) {
      $model = $modelClass::findOne($id);
    }
    if(isset($model)){
      return $model;
    }else {
      throw new NotFoundHttpException("Object not found: $id");
    }
  }
}

view

view 動(dòng)作不需要改動(dòng),因?yàn)?model 有 getRelated 的自有機(jī)制

class ViewAction extend Action
{
  public function run($id)
  {
    $model = $this->findModel($id);
    if($this->checkAccess) {
      call_user_func($this->checkAccess, $this->id, $model);
    }
  }
}

update

public function run($id)
{
  /* @var $model ActiveRecord */
  $model = $this->findModel($id);
  if ($this->checkAccess) {
   call_user_func($this->checkAccess, $this->id, $model);
  }
  $model->scenario = $this->scenario;
  $model->load(Yii::$app->getRequest()->getBodyParams(), '');
  $model->save();
  return $model;
}

經(jīng)過(guò)改造后,需要滿(mǎn)足對(duì)關(guān)聯(lián)模型的update動(dòng)作

public function run($id)
{
  /* @var $model ActiveRecord */
  $model = $this->findModel($id);
  if ($this->checkAccess) {
   call_user_func($this->checkAccess, $this->id, $model);
  }
  $model->scenario = $this->scenario;
    /*
     *
     * x-www-form-urlencoded key=>value
     * image mmmmmmmm
     * link nnnnnnnnnn
     * newsItem[title]=>ttttttttttt , don't use newsItem["title"]
     * newsItem[body]=>bbbbbbbbbbb
     * don't use newsItem=>array("title":"tttttt","body":"bbbbbbb")
     * don't use newsItem=>{"title":"ttttttt","body":"bbbbbbbb"}
     *
     */
    $newsItem = Yii::$app->getRequest()->getBodyParams()['newsItem'];
    /*
      Array
      (
        [title] => ttttttttttt
        [body] => bbbbbbbbbbb
      )
     */
    $model->newsItem->load($newsItem, '');
    #$model->newsItem->load(Yii::$app->getRequest()->getBodyParams(), '');
    #print_R($model->newsItem);exit;
    #print_R($model->newsItem);exit;
    if($model->save())
    {
      $model->load(Yii::$app->getRequest()->getBodyParams(), '');
      $model->newsItem->save();
    }
  return $model;
}

這里還應(yīng)該對(duì) newsItem save 失敗 的情況進(jìn)行處理,暫且不處理。

關(guān)于“Yii2中Restful API的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI