Yii2 Restful API 原理分析
2015-06-08 08:54
701 查看
Yii2 有个很重要的特性是对
本文通过分析rest部分源码,简单剖析下yii2 实现 restful 的原理,并通过一些定制实现 对 关联模型的RESTful api 操作。
1. rest/Controller ~
它在一个API请求的控制周期中一次实现了下面的步骤
解析响应的内容格式
校验请求方法
检验用户权限
限制速度
格式化响应数据
behaviors
contentNegotiator
verbFilter
authenticator
rateLimiter
afterAction
serializeData
verbs
2. rest/ActiveController ~
默认的, 支持下面的方法:
可以通过覆盖
要增加一个新的动作, 覆盖
注意一点,确保你同时也覆盖了
也需要覆盖
根据上面的说明再写一遍
下面来实现一个继承自 这个
定义到这里就足够实现 rest\ActiveController 里面的默认方法了
下面来覆盖下,实现一些定制的方法
我这里以要实现对
这里还应该对
Restful API的默认支持, 通过短短的几个配置就可以实现简单的对现有
Model的
RESTful API
本文通过分析rest部分源码,简单剖析下yii2 实现 restful 的原理,并通过一些定制实现 对 关联模型的RESTful api 操作。
~代表
extends from的关系
| | 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 控制器类的基类
它在一个API请求的控制周期中一次实现了下面的步骤
1~5:
解析响应的内容格式
校验请求方法
检验用户权限
限制速度
格式化响应数据
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实现了一系列的和
ActiveRecord互通数据的RESTful方法
ActiveRecord的类名由
modelClass变量指明,
yii\db\ActiveRecordInterface???
默认的, 支持下面的方法:
* - `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响应的
action来禁用这些默认的动作。
要增加一个新的动作, 覆盖
actions()向其末尾增加一个新的
action class或者 是一个新的
action method
注意一点,确保你同时也覆盖了
verbs()方法来声明这个新的动作支持那些
HTTP Method
也需要覆盖
checkAccess()来检查当前用户是否有权限来执行响应的某个动作。
根据上面的说明再写一遍
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 = []) { } }
下面来实现一个继承自 这个
rest\ActiveController的
News控制器:
namespace app\controllers; use app\controllers\rest\ActiveController; #刚才这个AC,我从yii/rest下面拷贝了一份出来 class NewsController extends ActiveController { public $modelClass ='app\models\News'; }
定义到这里就足够实现 rest\ActiveController 里面的默认方法了
下面来覆盖下,实现一些定制的方法
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 进行大的改动,建议拷贝一份出来,不要使用原始的yii\rest\XXXAction命名空间
我这里以要实现对
related models进行 CURD 操作为目标进行大的改动
Action
在定制各个action之前, 先看看它们的基类
rest\Action, 主要是一个
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动作不需要改动,因为
model有
getRelated的自有机制
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; }
经过改造后,需要满足对关联模型的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; /* * * 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; }
这里还应该对
newsItemsave 失败 的情况进行处理,暂且不处理。
相关文章推荐
- Yii2的RESTful API开发
- Yii2 API认证和授权
- Yii2 XSS 防范策略
- Yii2详解Model类
- php正则表达式—对于文字匹配的建议
- 从Yii2的Request看其CSRF防范策略
- Yii2为ActiveForm的widget组件设置属性
- Yii2中的OAuth扩展及QQ互联登录
- Yii2 Unable to verify your data submission.
- Yii2 使用Ajax自动获取表单数据
- Yii2 DropDownList用法
- Yii 2发送带附件的邮件
- Yii2上传文件
- Yii2 实现上下联动的下拉框
- Yii2同时搜索多个字段
- Yii2让关联字段支持搜索功能
- Yii2中多表关联查询(hasOne、hasMany、join、joinwith)
- Yii2关联查询用法
- Yii2基本的访问控制AccessControl的一些理解
- Yii获取当前url和域名的方法