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. 【Web】前台传送JSON格式数据到后台Shell处理

    1.js中的json对象和字符串之间的转化:http://www.oschina.net/code/snippet_175925_6288 代码片段: var obj = JSON.parse(des ...

  2. python读取中文

    如何从文件中读取300个汉字? 看起来很简单,但很容易掉坑里了. 一开始我这么写: try: fd = codecs.open(os.path.join(settings.TEXT_CONTENT_D ...

  3. Centos6.8配置svn

    svn的安装:yum -y install subversion 一.一个仓库放所有的项目 创建仓库,以后所有代码都放在这个下面,创建成功后在svn下面多了几个文件夹.1.创建仓库:svnadmin ...

  4. 【4.29安恒杯】writeup

    #### 安恒杯_writeup 下面为比赛中做出的题目 MISC: SHOW ME THE FLAG-by-cyyzore CRYPTO: LAZYATTACK-by-GoldsNow 这一题非常巧 ...

  5. 【ACM】Crixalis's Equipment

      #include "stdio.h" #include "stdlib.h" /* 贪心算法: Ai->x 表示第i个物品的体积 Ai->y 表 ...

  6. WebService 之 WSDL详解

    WSDL (Web Services Description Language,Web服务描述语言)是一种XML Application,他将Web服务描述定义为一组服务访问点,客户端可以通过这些服务 ...

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

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

  8. C++ STL中Map的按Value排序

    那么我们如何实现对pair按value进行比较呢? 第一种:是最原始的方法,写一个比较函数:  第二种:刚才用到了,写一个函数对象.这两种方式实现起来都比较简单. typedef pair<st ...

  9. js设置加载进度提示

      CreateTime--2017年8月23日09:17:46Author:Marydon js设置加载进度提示 第一部分:CSS /*加载样式*/ .Loading { position: abs ...

  10. 解决 SQL Server2012附加出错的问题

    附加数据库出错 无法打开文件号 0 的文件 操作系统错误 根据错误提示:权限不足,添加相应权限就OK 步骤:1,右键单击要附加的数据库,选择安全选项卡选择用户:“Authenticated Users ...