Yii2中Restful API原理实例分析
本文实例分析了Yii2中RestfulAPI原理。分享给大家供大家参考,具体如下:
Yii2有个很重要的特性是对RestfulAPI的默认支持,通过短短的几个配置就可以实现简单的对现有Model的RESTfulAPI
这里通过分析rest部分源码,简单剖析下yii2实现restful的原理,并通过一些定制实现对关联模型的RESTfulapi操作。
~代表extendsfrom的关系
||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是RESTfulAPI控制器类的基类
它在一个API请求的控制周期中一次实现了下面的步骤1~5:
①解析响应的内容格式
②校验请求方法
③检验用户权限
④限制速度
⑤格式化响应数据
useyii\filters\auth\CompositeAuth; useyii\filters\ContentNegotiator; useyii\filters\RateLimiter; useyii\web\Response; useyii\filters\VerbFilter; /** *ControlleristhebaseclassforRESTfulAPIcontrollerclasses. * *ControllerimplementsthefollowingstepsinaRESTfulAPIrequesthandlingcycle *1.Resolvingresponseformat(see[[ContentNegotiator]]); *2.Validatingrequestmethod(see[[verbs()]]). *3.Authenticatinguser(see[[\yii\filters\auth\AuthInterface]]); *4.Ratelimiting(see[[RateLimiter]]); *5.Formattingresponsedata(see[[serializeData()]]) behaviors contentNegotiator verbFilter authenticator rateLimiter afterAction serializeDataYii::createObject($this->serializer)->serialize($data) verbs[] */ classControllerextends\yii\web\Controller { public$serializer='yii\rest\Serializer'; public$enableCsrfValidation=false; publicfunctionbehaviors() { 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(), ], ] } publicfunctionverbs() { return[]; } publicfunctionserializeData($data) { returnYii::createObject($this->serializer)->serialize($data); } publicfunctionafterAction($action,$result) { $result=parent::afterAction($action,$result); return$this->serializeData($result); } }
2.rest/ActiveController~rest/Controller
ActiveController实现了一系列的和ActiveRecord互通数据的RESTful方法
ActiveRecord的类名由modelClass变量指明,yii\db\ActiveRecordInterface???
默认的,支持下面的方法:
*-`index`:listofmodels
*-`view`:returnthedetailsofamodel
*-`create`:createanewmodel
*-`update`:updateanexistingmodel
*-`delete`:deleteanexistingmodel
*-`options`:returntheallowedHTTPmethods
可以通过覆盖actions()并且unsetting响应的action来禁用这些默认的动作。
要增加一个新的动作,覆盖actions()向其末尾增加一个新的actionclass或者是一个新的actionmethod
注意一点,确保你同时也覆盖了verbs()方法来声明这个新的动作支持那些HTTPMethod
也需要覆盖checkAccess()来检查当前用户是否有权限来执行响应的某个动作。
根据上面的说明再写一遍Controller
classActiveControllerextendsController { public#modelClass; public$updateScenario=Model::SCENARIO_DEFAULT; public$createScenario=Model::SCENARIO_DEFAULT; publicfunctioninit() { parent::init(); if($this->modelClass==null){ thrownewInvalidConfigException('The"modelClass"propertymustbeset.'); } } publicfunctionactions() { return[ 'index'=>[ 'class'=>'app\controllers\rest\IndexAction', 'modelClass'=>$this->modelClass, 'checkAccess'=>[$this,'checkAccess'], ], 'view'... 'create'... 'update'... 'delete'... 'options'... ]; } protectedfunctionverbs() { return[ 'index'=>['GET','HEAD'], 'view'=>['GET','HEAD'], 'create'=>['POST'], 'update'=>['PUT','PATCH'], 'delete'=>['DELETE'], ]; } publicfunctioncheckAccess($action,$model=null,$params=[]) { } }
下面来实现一个继承自这个rest\ActiveController的News控制器:
namespaceapp\controllers; useapp\controllers\rest\ActiveController;#刚才这个AC,我从yii/rest下面拷贝了一份出来 classNewsControllerextendsActiveController { public$modelClass='app\models\News'; }
定义到这里就足够实现rest\ActiveController里面的默认方法了
下面来覆盖下,实现一些定制的方法
classNewsControllerextendsActiveController { public$modelClass='app\models\News'; #定制serializer #public$serializer='yii\rest\Serializer'; public$serializer=[ 'class'=>'app\controllers\rest\Serializer', 'collectionEnvelope'=>'items', ]; publicfunctionbehaviors() { $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; } publicfunctioncheckAccess() { } }
3.定制Actions
如果要对Actions进行大的改动,建议拷贝一份出来,不要使用原始的yii\rest\XXXAction命名空间
我这里以要实现对relatedmodels进行CURD操作为目标进行大的改动
Action
在定制各个action之前,先看看它们的基类rest\Action,主要是一个findModel的方法
classActionextend\yii\base\Action { public$modelClass; public$findModel; public$checkAccess; publicfunctioninit() { if($this->modelClass==null){ thrownewInvalidConfigException(get_class($this).'::$modelClassmustbeset'); } } publicfunctionfindModel($id) { if($this->findModel!==null){ returncall_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{ thrownewNotFoundHttpException("Objectnotfound:$id"); } } }
view
view动作不需要改动,因为model有getRelated的自有机制
classViewActionextendAction { publicfunctionrun($id) { $model=$this->findModel($id); if($this->checkAccess){ call_user_func($this->checkAccess,$this->id,$model); } } }
update
publicfunctionrun($id) { /*@var$modelActiveRecord*/ $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; }
经过改造后,需要满足对关联模型的update动作
publicfunctionrun($id) { /*@var$modelActiveRecord*/ $model=$this->findModel($id); if($this->checkAccess){ call_user_func($this->checkAccess,$this->id,$model); } $model->scenario=$this->scenario; /* * *x-www-form-urlencodedkey=>value *imagemmmmmmmm *linknnnnnnnnnn *newsItem[title]=>ttttttttttt,don'tusenewsItem["title"] *newsItem[body]=>bbbbbbbbbbb *don'tusenewsItem=>array("title":"tttttt","body":"bbbbbbb") *don'tusenewsItem=>{"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; }
这里还应该对newsItemsave失败的情况进行处理,暂且不处理。
更多关于Yii相关内容感兴趣的读者可查看本站专题:《Yii框架入门及常用技巧总结》、《php优秀开发框架总结》、《smarty模板入门基础教程》、《php面向对象程序设计入门教程》、《php字符串(string)用法总结》、《php+mysql数据库操作入门教程》及《php常见数据库操作技巧汇总》
希望本文所述对大家基于Yii框架的PHP程序设计有所帮助。