本文實(shí)例分析了Yii2中Restful API原理。分享給大家供大家參考,具體如下:
Yii2 有個(gè)很重要的特性是對 Restful API的默認(rèn)支持, 通過短短的幾個(gè)配置就可以實(shí)現(xiàn)簡單的對現(xiàn)有Model的RESTful API
這里通過分析rest部分源碼,簡單剖析下yii2 實(shí)現(xiàn) restful 的原理,并通過一些定制實(shí)現(xiàn) 對 關(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 控制器類的基類
它在一個(gè)API請求的控制周期中一次實(shí)現(xiàn)了下面的步驟 1~5:
① 解析響應(yīng)的內(nèi)容格式
② 校驗(yàn)請求方法
③ 檢驗(yàn)用戶權(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 rateLimiterafterAction 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 的類名由 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
可以通過覆蓋 actions() 并且 unsetting 響應(yīng)的 action 來禁用這些默認(rèn)的動(dòng)作。
要增加一個(gè)新的動(dòng)作, 覆蓋 actions() 向其末尾增加一個(gè)新的 action class 或者 是一個(gè)新的 action method
注意一點(diǎn),確保你同時(shí)也覆蓋了 verbs() 方法來聲明這個(gè)新的動(dòng)作支持那些HTTP Method
也需要覆蓋checkAccess() 來檢查當(dāng)前用戶是否有權(quán)限來執(zhí)行響應(yīng)的某個(gè)動(dòng)作。
根據(jù)上面的說明再寫一遍 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 = []) { }}下面來實(shí)現(xiàn)一個(gè)繼承自 這個(gè)rest/ActiveController的 News 控制器:
namespace app/controllers;use app/controllers/rest/ActiveController; #剛才這個(gè)AC,我從yii/rest下面拷貝了一份出來class NewsController extends ActiveController{ public $modelClass ='app/models/News';} 定義到這里就足夠?qū)崿F(xiàn) rest/ActiveController 里面的默認(rèn)方法了
下面來覆蓋下,實(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
如果要對 Actions 進(jìn)行大的改動(dòng),建議拷貝一份出來,不要使用原始的 yii/rest/XXXAction命名空間
我這里以要實(shí)現(xiàn)對related models進(jìn)行 CURD 操作為目標(biāo)進(jìn)行大的改動(dòng)
Action
在定制各個(gè)action之前, 先看看它們的基類 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ā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)該對 newsItem save 失敗 的情況進(jìn)行處理,暫且不處理。
希望本文所述對大家基于Yii框架的PHP程序設(shè)計(jì)有所幫助。
新聞熱點(diǎn)
疑難解答
圖片精選