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程序设计有所帮助。