Yii2 有个很重要的特性是对 Restful API的默认支持, 通过短短的几个配置就可以实现简单的对现有ModelRESTful API

参考另一篇文章: http://www.cnblogs.com/ganiks/p/yii2-restful-api-dev.html

本文通过分析rest部分源码,简单剖析下yii2 实现 restful 的原理,并通过一些定制实现 对 关联模型的RESTful api 操作。

原创文章, 转载请注明 http://www.cnblogs.com/ganiks/

~ 代表 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

  1. 解析响应的内容格式
  2. 校验请求方法
  3. 检验用户权限
  4. 限制速度
  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);
}
}
  1. 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\ActiveControllerNews 控制器:

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()
{
}
}
  1. 定制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 动作不需要改动,因为 modelgetRelated 的自有机制

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

原创文章, 转载请注明 http://www.cnblogs.com/ganiks/

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;
}

这里还应该对 newsItem save 失败 的情况进行处理,暂且不处理。

Yii2 Restful API 原理分析的更多相关文章

  1. Yii2 Restful Api 401

    采用Yii2 Restful Api方式为APP提供数据,默认你已经做好了所有的编码和配置工作.采用Postman测试接口: 出现这个画面的一个可能原因是:access_token的写法有误,如果你使 ...

  2. Yii2 restful api创建,认证授权以及速率控制

    Yii2 restful api创建,认证授权以及速率控制 下面是对restful从创建到速率控制的一个详细流程介绍,里面的步骤以及截图尽可能详细,熟悉restful的盆友可能觉得过于繁琐,新手不妨耐 ...

  3. yii2 RESTful API 405 Method Not Allowed

    关于 Yii2 中 RESTful API 的开发,可以参考另一篇随笔 http://www.cnblogs.com/ganiks/p/yii2-restful-api-dev.html 测试的过程中 ...

  4. Yii2 Restful api设计--App接口编程

    Yii2框架写一套RESTful风格的API,对照魏曦教你学 一,入门 一.目录结构 实现一个简单地RESTful API只需用到三个文件.目录如下: frontend ├─ config │ └ m ...

  5. yii2 restful api——app接口编程实例

    <?php namespace common\components; use common\models\Cart; use common\models\User; use Yii; use y ...

  6. yii2 RESTful API Develop

    参考文档:http://www.yiiframework.com/doc-2.0/guide-rest.html 以 DB 中的 news 表为例创建该资源的 RESTful API,最终的测试通过工 ...

  7. yii2 RESTful api的详细使用

    作者:白狼 出处:http://www.manks.top/yii2-restful-api.html 本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则 ...

  8. yii2 restful api --app接口编程

    转 http://www.yiichina.com/tutorial/1143yii2中restful url访问配置, 登陆接口access-token验证类 [ 2.0 版本 ] 登陆接口acce ...

  9. 快速创建yii2 RESTful api的小记

    1.复制backend的应用到同级目录,改名叫api 2.然后就是配置项修改,common和api目录下的 common下: bootstrap.php最后添加一行配置 api/config/main ...

随机推荐

  1. js正则表达式之中文验证(转)

    原文地址:http://houfeng0923.iteye.com/blog/1035321 今天做表单提交的输入框条件验证,验证是否包含中文:网上搜了一圈基于js正则表达式的验证基本不好用,而且大多 ...

  2. J2EE,LAMP和ASP.NET三者比较

    为了开发一个动态网站出来,有3中选择,三种选择是一种三足鼎立的一种状态:LAMP   J2EE  .NET,各有各的好,下面自己总结一下 1.ASP.NET ASP.NET 的开发框架是 Window ...

  3. subline 配置,本地项目代码下断点来调试远程项目

    参考: https://my.oschina.net/ptk/blog/299464 1. 文件 tts.sublime-project 的配置如下: { "folders": [ ...

  4. ActiveMQ订阅模式持久化实现

    实现步骤:1.配置发送xml,applicationContext-send.xml <?xml version="1.0" encoding="UTF-8&quo ...

  5. DevExpress:下拉框绑定数据源 (ComboBoxEdit,LookUpEdit)

    DevExpress:下拉框绑定数据源 (ComboBoxEdit,LookUpEdit) DevExpress:下拉框绑定数据源 (ComboBoxEdit,LookUpEdit) // 设置下拉框 ...

  6. 假设这篇文章也能訪问过万,我的那些朋友就是SB

    能和我这个疯子交朋友的一定是神经病

  7. HTML 超级链接详细讲解

    超级链接 超级链接是网站中使用比较频繁的HTML元素,因为网站的各种页面都是由超级链接串接而成,超级链接完成了页面之间的跳转.超级链接是浏览者和服务器的交互的主要手段,在后面的技术中会逐步深化学习. ...

  8. WinServer 之 内网发布网站后端口映射外网访问

    内网IP只能在内网局域网访问连接,在外网是不能认识内网IP不能访问的.如有路由权限,且路由有固定公网IP,可以通过路由的端口映射,实现外网访问内网.如无路由,或路由无公网IP,需要用到第三方开放的花生 ...

  9. C/C++中传值和传地址(引用)

    C/C++中参数传递有两种方式,传值或传地址(传引用),通常我们要在被调用函数中改变一个变量的值就需要传地址调用方式,例如: void swap_by_value(int a, int b) { in ...

  10. iOS多线程之NSOperation和NSOperationQueue的使用

    一:NSOperation 两个子类+重写main方法 NSInvocationOperation NSBlockOperation 有个类方法 BlockOprationWith: 还有就是自己个子 ...