yii2 restful api 风格搭建(一)

最近在研究如何利用 yii2 搭建 restful api,将 yii2 restful api / yii2 rest api 搭建心得写下,欢迎一起讨论
使用yii2.0.13 advanced 版,将 frontend 整个作为 api 接口项目,除了接口的路由规则可以认证通过外,其他的路由规则都返回请求错误的格式

1、数据库结构
CREATE TABLE `goods` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `price` int(11) unsigned NOT NULL,
  `status` tinyint(1) unsigned NOT NULL,
  `create_time` int(11) unsigned NOT NULL,
  `modify_time` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
2、使用 gii 创建 goods model
3、创建 api modules

在 frontend 下新建文件夹 modules
使用 gii 在 modules 文件中创建 v1 module(防止以后接口更新替换时向前兼容,接口增加 v1 v2 等版本控制)

4、将 v1 moduel 写到配置文件中
'modules' => [
    'v1' => [
        'class' => 'frontend\modules\v1\Module',
    ],
],
5、修改 frontend 下 main.php 中,user 的配置(以下说的配置文件,都是 frontend 下 main.php)
'user' => [
    'identityClass'     => 'frontend\models\User',
    'enableAutoLogin'   => false,
    'enableSession'     => false,
    'loginUrl'          => null,
],
6、新建 frontend\models\User,继承 common\models\User
<?php

namespace frontend\models;

class User extends \common\models\User
{

}
7、启用并修改配置文件中的 urlManager

调试 urlManager 的时候要小心,他会将生成好的路由写入缓存(默认是文件缓存),有些更改可能不会立马生效

'urlManager' => [
    //用于表明 urlManager 是否启用 URL 美化功能
    //默认不启用。但实际使用中,特别是产品环境,一般都会启用
    'enablePrettyUrl'       => true,
    //是否启用严格解析,如启用严格解析,要求当前请求应至少匹配1个路由规则,否则认为是无效路由。
    //这个选项仅在 enablePrettyUrl 启用后才有效。
    //如果开启,表示只有配置在 rules 里的规则才有效
    //由于项目会将一些 url 进行优化,所以这里需要设置为 true
    'enableStrictParsing'   => true,
    //指定是否在URL在保留入口脚本 index.php
    'showScriptName'        => false,
    'rules' => [
        //当然,如果自带的路由无法满足需求,可以自己增加规则
        'GET <module:(v)\d+>/<controller:\w+>/search' => '<module>/<controller>/search',
        [
            'class'         => 'yii\rest\UrlRule',
            'controller'    => ['v1/goods'],
            // 由于 resetful 风格规定 URL 保持格式一致并且始终使用复数形式
            // 所以如果你的 controller 是单数的名称比如 UserController
            // 设置 pluralize 为 true (默认为 true)的话,url 地址必须是 users 才可访问
            // 如果 pluralize 设置为 false, url 地址必须是 user 也可访问
            // 如果你的 controller 本身是复数名称 UsersController ,此参数没用,url 地址必须是 users
            'pluralize'     => false,
        ],
    ],
],
8、去掉配置文件中的 errorHandler 配置(整个 frontend 都是接口,不需要 html 的响应格式)
9、将内容协商配置到引导文件中(因为整个 frontend 都需要)
'bootstrap' => [
    'log',
    //全局内容协商
    [
        //ContentNegotiator 类可以分析request的header然后指派所需的响应格式给客户端,不需要我们人工指定
        'class'     => 'yii\filters\ContentNegotiator',
        'formats' => [
            'application/json' => yii\web\Response::FORMAT_JSON,
            'application/xml' => yii\web\Response::FORMAT_XML,
            //api 端目前只需要json 和 xml
            //还可以增加 yii\web\Response 类内置的响应格式,或者自己增加响应格式
        ],
    ]
],
10、配置文件中,components 配置 response,返回格式
'response' => [
    'class'     => 'yii\web\Response',
    //设置 api 返回格式,错误码不在 header 里实现,而是放到 body里
    'as resBeforeSend' => [
        'class'         => 'frontend\extensions\ResBeforeSendBehavior',
        'defaultCode'   => 500,
        'defaultMsg'    => 'error',
    ],
    //ps:components 中绑定事件,可以用两种方法
    //'on eventName' => $eventHandler,
    //'as behaviorName' => $behaviorConfig,
    //参考 http://www.yiiframework.com/doc-2.0/guide-concept-configurations.html#configuration-format
],
11、编写 frontend\extensions\ResBeforeSendBehavior 代码
<?php

namespace frontend\extensions;

use Yii;
use yii\web\Response;
use yii\base\Behavior;

class ResBeforeSendBehavior extends Behavior{

    public $defaultCode = 500;

    public $defaultMsg = 'error';

    // 重载events() 使得在事件触发时,调用行为中的一些方法
    public function events() {
        // 在 EVENT_BEFORE_SEND 事件触发时,调用成员函数 beforeSend
        return [
            Response::EVENT_BEFORE_SEND => 'beforeSend',
        ];
    }

    // 注意 beforeSend 是行为的成员函数,而不是绑定的类的成员函数。
    // 还要注意,这个函数的签名,要满足事件 handler 的要求。
    public function beforeSend($event)
    {
        try {
            $response = $event->sender;
            if($response->data === null){
                $response->data = [
                    'code'  => $this->defaultCode,
                    'msg'   => $this->defaultMsg,
                ];
            } elseif(!$response->isSuccessful) {
                $exception = Yii::$app->getErrorHandler()->exception;
                if(is_object($exception) && !$exception instanceof yii\web\HttpException){
                    throw $exception;
                } else {
                    $rData = $response->data;
                    $response->data = [
                        'code'  => empty($rData['status']) ? $this->defaultCode : $rData['status'],
                        'msg'   => empty($rData['message']) ? $this->defaultMsg : $rData['message'],
                    ];
                }
            } else {
                /**
                 * $response->isSuccessful 表示是否会抛出异常
                 * 值为 true, 代表返回数据正常,没有抛出异常
                 */
                $rData = $response->data;
                $response->data = [
                    'code' => isset($rData['error_code']) ? $rData['error_code'] : 0,
                    'msg' => isset($rData['res_msg']) ? $rData['res_msg'] : $rData,
                ];
                $response->statusCode = 200;
            }
        } catch (\Exception $e) {
            $response->data = [
                'code'  => $this->defaultCode,
                'msg'   => $this->defaultMsg,
            ];
        }
        return true;
    }
}
12、创建 GoodsController.php
<?php

namespace frontend\modules\v1\controllers;  
  
use yii\rest\ActiveController;
  
class GoodsController extends ActiveController 
{  
    public $modelClass = 'common\models\Goods';

    public function actionSearch(){
        return [
            'error_code'    => 20,
            'res_msg'       => 'ok',
        ];
    }
}
13、应用入口同级增加.htaccess文件,隐藏index.php,以apache为例
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* index.php
14、测试
命令:
curl -s -H Accept:application/xml http://local.rest.com/v1/goods/1
返回:
<?xml version="1.0" encoding="UTF-8"?>
<response>
  <code>0</code>
  <msg>
    <id>1</id>
    <name>测试商品1</name>
    <price>600</price>
    <status>1</status>
    <create_time>1520490595</create_time>
    <modify_time>1520490595</modify_time>
  </msg>
</response>
命令:
curl -s -H Accept:application/json http://local.rest.com/v1/goods/1
返回:
{
  "code":0,
  "msg":{
    "id":"1",
    "name":"测试商品1",
    "price":"600",
    "status":1,
    "create_time":"1520490595",
    "modify_time":"1520490595"
  }
}
命令:
curl -s -H Accept:application/json http://local.rest.com/v1/goods11
返回:
{"code":404,"msg":"Page not found."}
命令:
curl -s -H Accept:application/json http://local.rest.com/v1/goods/search
返回:
{"code":20,"msg":"ok"}
15、参考

http://www.yiichina.com/doc/guide/2.0/rest-quick-start
http://www.yiichina.com/doc/guide/2.0/runtime-handling-errors
http://www.yiiframework.com/doc-2.0/guide-concept-configurations.html#configuration-format
http://www.yii-china.com/post/detail/536.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,078评论 25 707
  • 标签(空格分隔): Yii2 1 什么是Restful Restful 是一种软件架构风格,提供了一组设计原则和约...
    ahcj_11阅读 3,910评论 0 1
  • 这段时间的娱乐版块真的是热闹非凡,可却负能量十足。 刚刚王宝强与马蓉离婚事件的热度降下来,随即张纪中和樊馨蔓两口子...
    静默心谷阅读 420评论 3 2
  • 上次说到谈到一个18万的大客户,但是技术不够、设备不够,拍不过来忍痛让给了别人。 做无人机这个项目也是很坎坷,先是...
    后宫猫三千阅读 503评论 1 7