基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建接口应用(实现 RESTful 风格的 Web Service 服务的 API),调整默认字符集为:utf8mb4,接口响应格式的调整,空数组自动转换为空对象,在接口应用中收集请求日志消息(1个请求对应1条日志消息)至数据库,且实现日志功能的相应接口:日志列表(设置数据过滤器以启用筛选器处理)、日志详情 (五) (1)
1、设置数据库的默认排序规则为:utf8mb4_unicode_ci,如图1
2、修改用于数据库连接的默认字符集为:utf8mb4,编辑开发环境下的配置文件,\environments\dev\common\config\main-local.php,编辑生产环境下的配置文件,\environments\prod\common\config\main-local.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?php return [ 'components' => [ 'db' => [ 'class' => 'yii\db\Connection' , 'dsn' => 'mysql:host=localhost;dbname=yii2advanced' , 'username' => 'root' , 'password' => '' , 'charset' => 'utf8mb4' , ], 'mailer' => [ 'class' => 'yii\swiftmailer\Mailer' , 'viewPath' => '@common/mail' , // send all mails to a file by default. You have to set // 'useFileTransport' to false and configure a transport // for the mailer to send real emails. 'useFileTransport' => true, ], ], ]; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php return [ 'components' => [ 'db' => [ 'class' => 'yii\db\Connection' , 'dsn' => 'mysql:host=localhost;dbname=yii2advanced' , 'username' => 'root' , 'password' => '' , 'charset' => 'utf8mb4' , ], 'mailer' => [ 'class' => 'yii\swiftmailer\Mailer' , 'viewPath' => '@common/mail' , ], ], ]; |
3、执行初始化命令,如图2
1 | .\init |
4、编辑数据库配置,\common\config\main-local.php
1 2 3 4 5 6 7 | 'db' => [ 'class' => 'yii\db\Connection' , 'dsn' => 'mysql:host=localhost;dbname=g-s-yii2-app-advanced' , 'username' => 'g-s-yii2-app-advanced' , 'password' => 'IADO0x7uK4UpaRRM' , 'charset' => 'utf8mb4' , ], |
5、清空数据库,执行数据库迁移命令,如图3
1 | .\yii migrate |
6、日志的数据库模式可以通过应用迁移来初始化,执行如下命令,如图4
1 | .\yii migrate --migrationPath=@yii/log/migrations/ |
7、浏览数据库表,user、log表的排序规则为:utf8_unicode_ci,如图5
8、新建一个数据库迁移文件,调整排序规则为:utf8mb4_unicode_ci,执行命令,如图6
1 | .\yii migrate/create update_table_options_to_log |
9、修改表(user、log)默认的字符集和所有字符列的字符集,编辑 \console\migrations\m180620_105204_update_table_options_to_log.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | <?php use yii\db\Migration; /** * Class m180620_105204_update_table_options_to_log */ class m180620_105204_update_table_options_to_log extends Migration { /** * {@inheritdoc} */ public function safeUp() { $tableOptions = null; if ( $this ->db->driverName === 'mysql' ) { $tableOptions = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB' ; $this ->execute( 'ALTER TABLE {{%user}} CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci' ); $this ->execute( 'ALTER TABLE {{%log}} CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci' ); } $this ->addCommentOnTable( '{{%user}}' , '用户' , $tableOptions ); $this ->addCommentOnTable( '{{%log}}' , '日志' , $tableOptions ); } /** * {@inheritdoc} */ public function safeDown() { echo "m180620_105204_update_table_options_to_log cannot be reverted.\n" ; return false; } /* // Use up()/down() to run migration code without a transaction. public function up() { } public function down() { echo "m180620_105204_update_table_options_to_log cannot be reverted.\n"; return false; } */ } |
10、一个 [[yii\log\DbTarget|database target]] 目标导出已经过滤的日志消息到一个数据的表里面,设置日志目标为 DbTarget
11、控制台应用的配置文件,\console\config\main.php,代码
1 2 3 4 5 6 7 8 9 10 | 'components' => [ 'log' => [ 'targets' => [ [ 'class' => 'yii\log\FileTarget' , 'levels' => [ 'error' , 'warning' ], ], ], ], ], |
12、控制台应用的配置文件,\console\config\main.php,设置日志目标为 DbTarget,编辑代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 'components' => [ 'log' => [ 'traceLevel' => YII_DEBUG ? 3 : 0, 'targets' => [ 'file' => [ 'class' => 'yii\log\FileTarget' , 'levels' => [ 'error' , 'warning' ], ], 'db' => [ 'class' => 'yii\log\DbTarget' , 'except' => [ '*' ], 'prefix' => function () { $url = !Yii:: $app ->request->isConsoleRequest ? Yii:: $app ->request->getUrl() : null; $user = Yii:: $app ->has( 'user' , true) ? Yii:: $app ->get( 'user' ) : null; $userId = $user ? $user ->getId(false) : '-' ; return sprintf( '[%s][%s][%s]' , Yii:: $app ->id, $url , $userId ); }, 'logVars' => [], ] ], ], ], |
13、接口应用的配置文件,\api\config\main.php,代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | <?php $params = array_merge ( require __DIR__ . '/../../common/config/params.php' , require __DIR__ . '/../../common/config/params-local.php' , require __DIR__ . '/params.php' , require __DIR__ . '/params-local.php' ); return [ 'id' => 'app-api' , 'basePath' => dirname(__DIR__), 'bootstrap' => [ 'log' , 'contentNegotiator' ], 'controllerNamespace' => 'api\controllers' , 'version' => '1.0.0' , 'components' => [ 'request' => [ 'csrfParam' => '_csrf-api' , ], 'user' => [ 'identityClass' => 'api\models\User' , 'enableSession' => false, 'loginUrl' => null, 'enableAutoLogin' => false, ], 'session' => [ // this is the name of the session cookie used for login on the api 'name' => 'advanced-api' , ], 'log' => [ 'traceLevel' => YII_DEBUG ? 3 : 0, 'targets' => [ [ 'class' => 'yii\log\FileTarget' , 'levels' => [ 'error' , 'warning' ], ], ], ], 'urlManager' => require __DIR__ . '/urlManager.php' , 'i18n' => [ 'translations' => [ 'model/*' => [ 'class' => 'yii\i18n\PhpMessageSource' , 'forceTranslation' => true, 'basePath' => '@common/messages' , 'fileMap' =>[ ], ], '*' => [ 'class' => 'yii\i18n\PhpMessageSource' , 'forceTranslation' => true, 'basePath' => '@api/messages' , 'fileMap' =>[ ], ], ], ], 'contentNegotiator' => [ 'class' => 'yii\filters\ContentNegotiator' , 'formats' => [ 'application/json' => yii\web\Response::FORMAT_JSON, 'application/xml' => yii\web\Response::FORMAT_XML, ], 'languages' => [ 'en-US' , 'zh-CN' , ], ], ], 'modules' => [ 'v1' => [ 'class' => api\modules\v1\Module:: class , ], ], 'params' => $params , ]; |
14、接口应用的配置文件,\api\config\main.php,设置日志目标为 DbTarget,编辑代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 'log' => [ 'traceLevel' => YII_DEBUG ? 3 : 0, 'targets' => [ 'file' => [ 'class' => 'yii\log\FileTarget' , 'levels' => [ 'error' , 'warning' ], ], 'db' => [ 'class' => 'yii\log\DbTarget' , 'categories' => [ 'api\behaviors\RequestLogBehavior::beforeRequest' , ], 'prefix' => function () { $url = !Yii:: $app ->request->isConsoleRequest ? Yii:: $app ->request->getUrl() : null; $user = Yii:: $app ->has( 'user' , true) ? Yii:: $app ->get( 'user' ) : null; $userId = $user ? $user ->getId(false) : '-' ; return sprintf( '[%s][%s][%s]' , Yii:: $app ->id, $url , $userId ); }, 'logVars' => [], ] ], ], |
15、删除数据库中的所有表,重新执行命令,如图7
1 2 | .\yii migrate --migrationPath=@yii/log/migrations/ .\yii migrate |
16、浏览数据库表,user、log表的排序规则为:utf8mb4_unicode_ci,且表列的排序规则也为:utf8mb4_unicode_ci,如图8
17、定义请求日志行为,触发 [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] 事件时,写入日志至数据库,新建 \api\behaviors\RequestLogBehavior.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <?php namespace api\behaviors; use Yii; use yii\base\Behavior; /** * Class RequestLogBehavior * @package api\behaviors * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class RequestLogBehavior extends Behavior { public function events() { return [ Yii:: $app ::EVENT_BEFORE_REQUEST => 'beforeRequest' , ]; } /** * @inheritdoc */ public function beforeRequest( $event ) { $url = !Yii:: $app ->request->isConsoleRequest ? Yii:: $app ->request->getUrl() : null; $requestQueryParams = Yii:: $app ->getRequest()->getQueryParams(); $requestBodyParams = Yii:: $app ->getRequest()->getBodyParams(); $user = Yii:: $app ->has( 'user' , true) ? Yii:: $app ->get( 'user' ) : null; $userId = $user ? $user ->getId(false) : '-' ; $message = [ 'url' => $url , 'requestQueryParams' => $requestQueryParams , 'requestBodyParams' => $requestBodyParams , 'userId' => $userId , '$_SERVER' => [ 'HTTP_ACCEPT_LANGUAGE' => $_SERVER [ 'HTTP_ACCEPT_LANGUAGE' ], 'HTTP_ACCEPT' => $_SERVER [ 'HTTP_ACCEPT' ], 'HTTP_HOST' => $_SERVER [ 'HTTP_HOST' ], 'REMOTE_ADDR' => $_SERVER [ 'REMOTE_ADDR' ], 'REQUEST_URI' => $_SERVER [ 'REQUEST_URI' ], 'REQUEST_METHOD' => $_SERVER [ 'REQUEST_METHOD' ], 'CONTENT_TYPE' => $_SERVER [ 'CONTENT_TYPE' ], ], ]; Yii::info(serialize( $message ), __METHOD__ ); } } |
18、基于配置将行为附加到应用主体,编辑 \api\config\main.php
1 2 3 4 5 | 'components' => [ ], 'as requestLog' => [ 'class' => api\behaviors\RequestLogBehavior:: class , ], |
19、查看日志表,控制台应用的运行未写入日志,因为通过 [[yii\log\Target::except|except]] 属性来设置所有分类作为黑名单,如图9
20、运行接口应用,在 Postman 上执行1个不存在的接口请求,响应如下1 2 3 4 5 6 7 | { "name": "Not Found", "message": "页面未找到。", "code": 0, "status": 404, "type": "yii\\web\\NotFoundHttpException" } |
21、运行接口应用,在 Postman 上执行3个已存在的接口请求,依次响应如下
1 2 3 4 5 6 7 | { "name": "Not Found", "message": "User ID: 1, does not exist", "code": 20002, "status": 404, "type": "yii\\web\\NotFoundHttpException" } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | { "code": 10000, "message": "Create user success", "data": { "username": "111111", "email": "111111@163.com", "password_hash": "$2y$13$2PjzCSRtyblFnpfgAW6HL.LUqVLzWqHcOtmKgttpcGtpXY6DtKRmy", "auth_key": "gz-Cv8BczFGy2dFyd8ULjA_m1FK56vST", "status": 10, "created_at": 1529564925, "updated_at": 1529564925, "id": 1 } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | { "code": 10000, "message": "获取用户列表成功", "data": { "items": [ { "id": 1, "username": "111111", "auth_key": "gz-Cv8BczFGy2dFyd8ULjA_m1FK56vST", "password_hash": "$2y$13$2PjzCSRtyblFnpfgAW6HL.LUqVLzWqHcOtmKgttpcGtpXY6DtKRmy", "password_reset_token": null, "email": "111111@163.com", "status": 10, "created_at": 1529564925, "updated_at": 1529564925 } ], "_links": { "self": { } }, "_meta": { "totalCount": 1, "pageCount": 1, "currentPage": 1, "perPage": 20 } } } |
22、查看日志表,接口应用的运行已写入日志,因为通过 [[yii\log\Target::categories|categories]] 属性来设置 api\behaviors\RequestLogBehavior::beforeRequest 分类作为白名单,因此仅有 api\behaviors\RequestLogBehavior::beforeRequest 分类下的日志被写入,由于总计执行了4次,写入了4条日志,如图10
23、实现日志功能的相应接口,打开网址:http://www.github-shuijingwan-yii2-app-advanced.localhost/gii/model ,选项,命名空间为common\models,此时需支持国际化,生成 \common\models\Log.php,如图11
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | <?php namespace common\models; use Yii; /** * This is the model class for table "{{%log}}". * * @property int $id * @property int $level * @property string $category * @property double $log_time * @property string $prefix * @property string $message */ class Log extends \yii\db\ActiveRecord { /** * @inheritdoc */ public static function tableName() { return '{{%log}}' ; } /** * @inheritdoc */ public function rules() { return [ [[ 'level' ], 'integer' ], [[ 'log_time' ], 'number' ], [[ 'prefix' , 'message' ], 'string' ], [[ 'category' ], 'string' , 'max' => 255], ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => Yii::t( 'model/log' , 'ID' ), 'level' => Yii::t( 'model/log' , 'Level' ), 'category' => Yii::t( 'model/log' , 'Category' ), 'log_time' => Yii::t( 'model/log' , 'Log Time' ), 'prefix' => Yii::t( 'model/log' , 'Prefix' ), 'message' => Yii::t( 'model/log' , 'Message' ), ]; } } |
24、新建 \common\logics\Log.php,在common/logics目录中的MySQL模型文件为业务逻辑相关,继承至 \common\models\Log 数据层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?php namespace common\logics; use Yii; /** * This is the model class for table "{{%log}}". * * @property int $id * @property int $level * @property string $category * @property double $log_time * @property string $prefix * @property string $message */ class Log extends \common\models\Log { } |
25、新建 \common\messages\en-US\model\log.php,支持目标语言为英语美国时的消息翻译
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php /** * Created by PhpStorm. * User: WangQiang * Date: 2018/06/21 * Time: 15:41 */ return [ 'ID' => 'ID' , 'Level' => 'Level' , 'Category' => 'Category' , 'Log Time' => 'Log Time' , 'Prefix' => 'Prefix' , 'Message' => 'Message' , ]; |
26、新建 \common\messages\zh-CN\model\log.php,支持目标语言为简体中文时的消息翻译
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php /** * Created by PhpStorm. * User: WangQiang * Date: 2018/06/21 * Time: 15:44 */ return [ 'ID' => 'ID' , 'Level' => '等级' , 'Category' => '分类' , 'Log Time' => '日志时间' , 'Prefix' => '前缀' , 'Message' => '消息' , ]; |
27、新建 \api\models\Log.php,在api/models目录中的MySQL模型文件为业务逻辑相关(仅与api相关),继承至 \common\logics\Log 逻辑层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?php /** * Created by PhpStorm. * User: WangQiang * Date: 2018/06/21 * Time: 15:50 */ namespace api\models; class Log extends \common\logics\Log { } |
28、新建 \api\modules\v1\models\Log.php,继承至 \api\models\Log.php
注:\api\modules\v1\models\Log(仅用于 v1 模块) > \api\models\Log(仅用于 api 应用) > \common\logics\Log.php(可用于 api、frontend 等多个应用) > \common\models\Log.php(仅限于 Gii 生成) > \yii\db\ActiveRecord
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php /** * Created by PhpStorm. * User: WangQiang * Date: 2018/06/21 * Time: 15:53 */ namespace api\modules\v1\models; class Log extends \api\models\Log { } |
29、实现日志功能的相应接口,编辑 \api\config\urlManager.php,仅支持列表与详情
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php return [ 'class' => yii\web\UrlManager:: class , 'enablePrettyUrl' => true, 'enableStrictParsing' => true, 'showScriptName' => false, 'rules' => [ [ 'class' => 'yii\rest\UrlRule' , 'controller' => [ 'v1/user' ], ], [ 'class' => 'yii\rest\UrlRule' , 'controller' => [ 'v1/log' ], 'only' => [ 'index' , 'view' ], ], ], ]; |
30、新建控制器类 \api\controllers\LogController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <?php /** * Created by PhpStorm. * User: WangQiang * Date: 2018/06/21 * Time: 15:29 */ namespace api\controllers; use yii\rest\ActiveController; class LogController extends ActiveController { public $serializer = [ 'class' => 'api\rests\log\Serializer' , 'collectionEnvelope' => 'items' , ]; /** * @inheritdoc */ public function actions() { $actions = parent::actions(); // 禁用"create"、"update"、"delete"、"options"动作 unset( $actions [ 'create' ], $actions [ 'update' ], $actions [ 'delete' ], $actions [ 'options' ]); $actions [ 'index' ][ 'class' ] = 'api\rests\log\IndexAction' ; $actions [ 'view' ][ 'class' ] = 'api\rests\log\ViewAction' ; return $actions ; } } |
31、新建 \api\modules\v1\controllers\LogController.php,通过指定 [[yii\rest\ActiveController::modelClass|modelClass]] 作为 api\modules\v1\models\Log, 控制器就能知道使用哪个模型去获取和处理数据
注:\api\modules\v1\controllers\LogController.php(仅用于 v1 模块) > \api\controllers\LogController.php(仅用于 api 应用) > \yii\rest\ActiveController
1 2 3 4 5 6 7 8 9 10 11 | <?php namespace api\modules\v1\controllers; /** * Log controller for the `v1` module */ class LogController extends \api\controllers\LogController { public $modelClass = 'api\modules\v1\models\Log' ; } |
32、复制目录 \api\rests\user 下的 Action.php、IndexAction.php、ViewAction.php、Serializer.php 至目录 \api\rests\log
33、编辑 \api\rests\log\IndexAction.php,调整命名空间、继承关系、查询条件等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | <?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace api\rests\log; use Yii; use yii\base\DynamicModel; use yii\data\ActiveDataProvider; /** * IndexAction implements the API endpoint for listing multiple models. * * For more details and usage information on IndexAction, see the [guide article on rest controllers](guide:rest-controllers). * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class IndexAction extends \yii\rest\IndexAction { /** * Prepares the data provider that should return the requested collection of the models. * @return ActiveDataProvider */ protected function prepareDataProvider() { $requestParams = Yii:: $app ->getRequest()->getBodyParams(); if ( empty ( $requestParams )) { $requestParams = Yii:: $app ->getRequest()->getQueryParams(); } /* 数据过滤器 */ $this ->dataFilter = [ 'class' => 'yii\data\ActiveDataFilter' , 'searchModel' => function () { return ( new DynamicModel([ 'level' => null, 'category' => null, 'log_time' => null, 'prefix' => null])) ->addRule( 'level' , 'integer' ) ->addRule([ 'category' , 'prefix' ], 'trim' ) ->addRule( 'log_time' , 'double' ) ->addRule([ 'category' , 'prefix' ], 'string' ); }, ]; $filter = null; if ( $this ->dataFilter !== null) { $this ->dataFilter = Yii::createObject( $this ->dataFilter); if ( $this ->dataFilter->load( $requestParams )) { $filter = $this ->dataFilter->build(); if ( $filter === false) { foreach ( $this ->dataFilter->getFirstErrors() as $message ) { $firstErrors = $message ; break ; } return [ 'code' => 20803, 'message' => Yii::t( 'error' , Yii::t( 'error' , Yii::t( 'error' , '20803' ), [ 'firstErrors' => $firstErrors ]))]; } } } if ( $this ->prepareDataProvider !== null) { return call_user_func( $this ->prepareDataProvider, $this , $filter ); } /* @var $modelClass \yii\db\BaseActiveRecord */ $modelClass = $this ->modelClass; $query = $modelClass ::find(); if (! empty ( $filter )) { $query ->andFilterWhere( $filter ); } return Yii::createObject([ 'class' => ActiveDataProvider::className(), 'query' => $query , 'pagination' => [ 'params' => $requestParams , ], 'sort' => [ 'params' => $requestParams , ], ]); } } |
34、编辑 \api\rests\log\Serializer.php,调整命名空间、继承关系、响应结构(响应成功:”code”: 10000,”message”,”data”;响应失败:”code”: 不等于10000的其他数字,”message”)等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | <?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace api\rests\log; use Yii; use yii\data\DataProviderInterface; /** * Serializer converts resource objects and collections into array representation. * * Serializer is mainly used by REST controllers to convert different objects into array representation * so that they can be further turned into different formats, such as JSON, XML, by response formatters. * * The default implementation handles resources as [[Model]] objects and collections as objects * implementing [[DataProviderInterface]]. You may override [[serialize()]] to handle more types. * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class Serializer extends \yii\rest\Serializer { /** * Serializes a data provider. * @param DataProviderInterface $dataProvider * @return array the array representation of the data provider. */ protected function serializeDataProvider( $dataProvider ) { if ( $this ->preserveKeys) { $models = $dataProvider ->getModels(); } else { $models = array_values ( $dataProvider ->getModels()); } $models = $this ->serializeModels( $models ); if (( $pagination = $dataProvider ->getPagination()) !== false) { $this ->addPaginationHeaders( $pagination ); } if ( $this ->request->getIsHead()) { return null; } elseif ( $this ->collectionEnvelope === null) { return $models ; } $result = [ $this ->collectionEnvelope => $models , ]; if ( empty ( $result [ 'items' ])) { return [ 'code' => 20801, 'message' => Yii::t( 'error' , '20801' )]; } foreach ( $result [ 'items' ] as $key => $item ) { $result [ 'items' ][ $key ][ 'message' ] = $item [ 'message' ] = unserialize( $item [ 'message' ]); if ( empty ( $item [ 'message' ][ 'userId' ])) { $result [ 'items' ][ $key ][ 'message' ][ 'userId' ] = '0' ; } if ( empty ( $item [ 'message' ][ 'requestQueryParams' ])) { $result [ 'items' ][ $key ][ 'message' ][ 'requestQueryParams' ] = (object)[]; } if ( empty ( $item [ 'message' ][ 'requestBodyParams' ])) { $result [ 'items' ][ $key ][ 'message' ][ 'requestBodyParams' ] = (object)[]; } } if ( $pagination !== false) { return [ 'code' => 10000, 'message' => Yii::t( 'success' , '10801' ), 'data' => array_merge ( $result , $this ->serializePagination( $pagination ))]; } return [ 'code' => 10000, 'message' => Yii::t( 'success' , '10801' ), 'data' => $result ]; } } |
35、编辑语言包文件:\api\messages\zh-CN\success.php(简体中文、响应成功)
1 2 3 4 5 6 7 8 9 10 11 | <?php return [ 10000 => 'success' , 10001 => '获取用户列表成功' , 10002 => '获取用户详情成功' , 10003 => '创建用户成功' , 10004 => '更新用户成功' , 10005 => '删除用户成功' , 10801 => '获取日志列表成功' , 10802 => '获取日志详情成功' , ]; |
36、编辑语言包文件:\api\messages\zh-CN\error.php(简体中文、响应失败)
1 2 3 4 5 6 7 8 9 10 11 | <?php return [ 20000 => 'error' , 20001 => '用户列表为空' , 20002 => '用户ID:{id},不存在' , 20003 => '用户ID:{id},的状态为已删除' , 20004 => '数据验证失败:{firstErrors}' , 20801 => '日志列表为空' , 20802 => '日志ID:{id},不存在' , 20803 => '数据过滤器验证失败:{firstErrors}' , ]; |
37、编辑语言包文件:\api\messages\en-US\success.php(英语美国、响应成功)
1 2 3 4 5 6 7 8 9 10 11 | <?php return [ 10000 => 'success' , 10001 => 'Get user list success' , 10002 => 'Get user details success' , 10003 => 'Create user success' , 10004 => 'Update user success' , 10005 => 'Delete user success' , 10801 => 'Get log list success' , 10802 => 'Get log details success' , ]; |
38、编辑语言包文件:\api\messages\en-US\error.php(英语美国、响应失败)
1 2 3 4 5 6 7 8 9 10 11 | <?php return [ 20000 => 'error' , 20001 => 'User list is empty' , 20002 => 'User ID: {id}, does not exist' , 20003 => 'User ID: {id}, status is deleted' , 20004 => 'Data validation failed: {firstErrors}' , 20801 => 'Log list is empty' , 20802 => 'Log ID: {id}, does not exist' , 20803 => 'Data filter validation failed: {firstErrors}' , ]; |
39、在 Postman 中,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/logs ,200响应,requestQueryParams、requestBodyParams的格式有时为数组(为空时),有时为对象,如图12
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | { "code": 10000, "message": "获取日志列表成功", "data": { "items": [ { "id": 1, "level": 4, "category": "api\\behaviors\\RequestLogBehavior::beforeRequest", "log_time": 1529564823.0948, "prefix": "[app-api][/v1/menus][]", "message": { "url": "/v1/menus", "requestQueryParams": {}, "requestBodyParams": {}, "userId": "0", "$_SERVER": { "HTTP_ACCEPT_LANGUAGE": "zh-CN", "HTTP_ACCEPT": "application/json; version=0.0; cookie=enable", "HTTP_HOST": "api.github-shuijingwan-yii2-app-advanced.localhost", "REMOTE_ADDR": "127.0.0.1", "REQUEST_URI": "/v1/menus", "REQUEST_METHOD": "GET", "CONTENT_TYPE": "application/x-www-form-urlencoded" } } }, { "id": 2, "level": 4, "category": "api\\behaviors\\RequestLogBehavior::beforeRequest", "log_time": 1529564916.6603, "prefix": "[app-api][/v1/users/1][]", "message": { "url": "/v1/users/1", "requestQueryParams": {}, "requestBodyParams": { "email": "222222@qq.com", "password": "222222", "status": "0" }, "userId": "0", "$_SERVER": { "HTTP_ACCEPT_LANGUAGE": "en-US", "HTTP_ACCEPT": "application/json; version=0.0", "HTTP_HOST": "api.github-shuijingwan-yii2-app-advanced.localhost", "REMOTE_ADDR": "127.0.0.1", "REQUEST_URI": "/v1/users/1", "REQUEST_METHOD": "PUT", "CONTENT_TYPE": "application/x-www-form-urlencoded" } } }, { "id": 3, "level": 4, "category": "api\\behaviors\\RequestLogBehavior::beforeRequest", "log_time": 1529564924.6648, "prefix": "[app-api][/v1/users][]", "message": { "url": "/v1/users", "requestQueryParams": {}, "requestBodyParams": { "email": "111111@163.com", "password": "111111", "username": "111111" }, "userId": "0", "$_SERVER": { "HTTP_ACCEPT_LANGUAGE": "en-US", "HTTP_ACCEPT": "application/json; version=0.0", "HTTP_HOST": "api.github-shuijingwan-yii2-app-advanced.localhost", "REMOTE_ADDR": "127.0.0.1", "REQUEST_URI": "/v1/users", "REQUEST_METHOD": "POST", "CONTENT_TYPE": "application/x-www-form-urlencoded" } } } ], "_links": { "self": { }, "next": { }, "last": { } }, "_meta": { "totalCount": 212, "pageCount": 71, "currentPage": 1, "perPage": 3 } } } |
40、在 Postman 中,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/logs?filter[level]=a ,200响应,如图13
1 2 3 4 | { "code": 20803, "message": "数据过滤器验证失败:Level必须是整数。" } |
41、在 Postman 中,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/logs?filter[level]=4&filter[category][like]=RequestLogBehavior&filter[prefix][like]=app-api&filter[log_time][gte]=1528090828&filter[log_time][lte]=1529564924.6648 ,测试数据过滤器,200响应,如图14
1 2 3 4 5 | filter[level]:4 filter[category][like]:RequestLogBehavior filter[prefix][like]:app-api filter[log_time][gte]:1528090828 filter[log_time][lte]:1529564924.6648 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | { "code": 10000, "message": "获取日志列表成功", "data": { "items": [ { "id": 1, "level": 4, "category": "api\\behaviors\\RequestLogBehavior::beforeRequest", "log_time": 1529564823.0948, "prefix": "[app-api][/v1/menus][]", "message": { "url": "/v1/menus", "requestQueryParams": {}, "requestBodyParams": {}, "userId": "0", "$_SERVER": { "HTTP_ACCEPT_LANGUAGE": "zh-CN", "HTTP_ACCEPT": "application/json; version=0.0; cookie=enable", "HTTP_HOST": "api.github-shuijingwan-yii2-app-advanced.localhost", "REMOTE_ADDR": "127.0.0.1", "REQUEST_URI": "/v1/menus", "REQUEST_METHOD": "GET", "CONTENT_TYPE": "application/x-www-form-urlencoded" } } }, { "id": 2, "level": 4, "category": "api\\behaviors\\RequestLogBehavior::beforeRequest", "log_time": 1529564916.6603, "prefix": "[app-api][/v1/users/1][]", "message": { "url": "/v1/users/1", "requestQueryParams": {}, "requestBodyParams": { "email": "222222@qq.com", "password": "222222", "status": "0" }, "userId": "0", "$_SERVER": { "HTTP_ACCEPT_LANGUAGE": "en-US", "HTTP_ACCEPT": "application/json; version=0.0", "HTTP_HOST": "api.github-shuijingwan-yii2-app-advanced.localhost", "REMOTE_ADDR": "127.0.0.1", "REQUEST_URI": "/v1/users/1", "REQUEST_METHOD": "PUT", "CONTENT_TYPE": "application/x-www-form-urlencoded" } } }, { "id": 3, "level": 4, "category": "api\\behaviors\\RequestLogBehavior::beforeRequest", "log_time": 1529564924.6648, "prefix": "[app-api][/v1/users][]", "message": { "url": "/v1/users", "requestQueryParams": {}, "requestBodyParams": { "email": "111111@163.com", "password": "111111", "username": "111111" }, "userId": "0", "$_SERVER": { "HTTP_ACCEPT_LANGUAGE": "en-US", "HTTP_ACCEPT": "application/json; version=0.0", "HTTP_HOST": "api.github-shuijingwan-yii2-app-advanced.localhost", "REMOTE_ADDR": "127.0.0.1", "REQUEST_URI": "/v1/users", "REQUEST_METHOD": "POST", "CONTENT_TYPE": "application/x-www-form-urlencoded" } } } ], "_links": { "self": { } }, "_meta": { "totalCount": 3, "pageCount": 1, "currentPage": 1, "perPage": 20 } } } |
1 | SELECT COUNT (*) FROM `log` WHERE (` level `= '4' ) AND (`category` LIKE '%RequestLogBehavior%' ) AND (`prefix` LIKE '%app-api%' ) AND ((`log_time` >= '1528090828' ) AND (`log_time` <= '1529564924.6648' )) |
42、定义一个搜索模型,此搜索模型应声明所有可用的搜索属性及其验证规则,新建 \common\logics\LogSearch.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <?php namespace common\logics; use Yii; use yii\base\Model; /** * LogSearch represents the model behind the search form about `common\models\Log`. */ class LogSearch extends Model { public $level ; public $category ; public $log_time ; public $prefix ; /** * @inheritdoc */ public function rules() { return [ [[ 'level' ], 'integer' ], [[ 'log_time' ], 'number' ], [[ 'prefix' , 'message' ], 'string' ], [[ 'category' , 'prefix' ], 'trim' ], ]; } } |
43、新建 \api\models\LogSearch.php,在api/models目录中的MySQL模型文件为业务逻辑相关(仅与api相关),继承至 \common\logics\LogSearch 逻辑层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?php /** * Created by PhpStorm. * User: WangQiang * Date: 2018/06/22 * Time: 13:43 */ namespace api\models; class LogSearch extends \common\logics\LogSearch { } |
44、新建 \api\modules\v1\models\LogSearch.php,继承至 \api\models\LogSearch.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php /** * Created by PhpStorm. * User: WangQiang * Date: 2018/06/22 * Time: 13:46 */ namespace api\modules\v1\models; class LogSearch extends \api\models\LogSearch { } |
45、编辑 \api\rests\log\IndexAction.php,取消使用 yii\base\DynamicModel实例作为$searchModel,设置数据过滤器以启用筛选器处理,生成SQL语句,如图15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | <?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace api\rests\log; use Yii; use yii\data\ActiveDataProvider; /** * IndexAction implements the API endpoint for listing multiple models. * * For more details and usage information on IndexAction, see the [guide article on rest controllers](guide:rest-controllers). * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class IndexAction extends \yii\rest\IndexAction { public $dataFilter = [ 'class' => 'yii\data\ActiveDataFilter' , 'searchModel' => 'api\modules\v1\models\LogSearch' , ]; /** * Prepares the data provider that should return the requested collection of the models. * @return ActiveDataProvider */ protected function prepareDataProvider() { $requestParams = Yii:: $app ->getRequest()->getBodyParams(); if ( empty ( $requestParams )) { $requestParams = Yii:: $app ->getRequest()->getQueryParams(); } $filter = null; if ( $this ->dataFilter !== null) { $this ->dataFilter = Yii::createObject( $this ->dataFilter); if ( $this ->dataFilter->load( $requestParams )) { $filter = $this ->dataFilter->build(); if ( $filter === false) { foreach ( $this ->dataFilter->getFirstErrors() as $message ) { $firstErrors = $message ; break ; } return [ 'code' => 20803, 'message' => Yii::t( 'error' , Yii::t( 'error' , Yii::t( 'error' , '20803' ), [ 'firstErrors' => $firstErrors ]))]; } } } if ( $this ->prepareDataProvider !== null) { return call_user_func( $this ->prepareDataProvider, $this , $filter ); } /* @var $modelClass \yii\db\BaseActiveRecord */ $modelClass = $this ->modelClass; $query = $modelClass ::find(); if (! empty ( $filter )) { $query ->andFilterWhere( $filter ); } return Yii::createObject([ 'class' => ActiveDataProvider::className(), 'query' => $query , 'pagination' => [ 'params' => $requestParams , ], 'sort' => [ 'params' => $requestParams , ], ]); } } |
1 | SELECT COUNT (*) FROM `log` WHERE (` level `= '4' ) AND (`category` LIKE '%RequestLogBehavior%' ) AND (`prefix` LIKE '%app-api%' ) AND ((`log_time` >= '1528090828' ) AND (`log_time` <= '1529564924.6648' )) |
46、GET /logs/1: 返回日志 1 的详细信息,编辑 \api\rests\log\Action.php,调整命名空间、继承关系、响应结构等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | <?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace api\rests\log; use Yii; use yii\db\ActiveRecordInterface; use yii\web\NotFoundHttpException; /** * Action is the base class for action classes that implement RESTful API. * * For more details and usage information on Action, see the [guide article on rest controllers](guide:rest-controllers). * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class Action extends \yii\rest\Action { /** * Returns the data model based on the primary key given. * If the data model is not found, a 404 HTTP exception will be raised. * @param string $id the ID of the model to be loaded. If the model has a composite primary key, * the ID must be a string of the primary key values separated by commas. * The order of the primary key values should follow that returned by the `primaryKey()` method * of the model. * @return ActiveRecordInterface the model found * @throws NotFoundHttpException if the model cannot be found */ public function findModel( $id ) { if ( $this ->findModel !== null) { return call_user_func( $this ->findModel, $id , $this ); } /* @var $modelClass ActiveRecordInterface */ $modelClass = $this ->modelClass; $keys = $modelClass ::primaryKey(); if ( count ( $keys ) > 1) { $values = explode ( ',' , $id ); if ( count ( $keys ) === count ( $values )) { $model = $modelClass ::findOne( array_combine ( $keys , $values )); } } elseif ( $id !== null) { $model = $modelClass ::findOne( $id ); } if (isset( $model )) { return $model ; } throw new NotFoundHttpException(Yii::t( 'error' , Yii::t( 'error' , Yii::t( 'error' , '20802' ), [ 'id' => $id ])), 20802); } } |
47、编辑 \api\rests\log\ViewAction.php,调整命名空间、继承关系、响应结构等。ContentNegotiator支持响应内容格式处理和语言处理。 通过检查 GET 参数和 Accept HTTP头部来决定响应内容格式和语言。配置ContentNegotiator支持英语美国和简体中文。配置响应组件,传递给 yii\helpers\Json::encode() 的编码选项,JSON_FORCE_OBJECT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE,JSON_FORCE_OBJECT:使一个非关联数组输出一个类(Object)而非数组。在数组为空而接受者需要一个类(Object)的时候尤其有用。避免手动处理空数组的转换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | <?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace api\rests\log; use Yii; /** * ViewAction implements the API endpoint for returning the detailed information about a model. * * For more details and usage information on ViewAction, see the [guide article on rest controllers](guide:rest-controllers). * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class ViewAction extends Action { /** * Displays a model. * @param string $id the primary key of the model. * @return \yii\db\ActiveRecordInterface the model being displayed */ public function run( $id ) { $model = $this ->findModel( $id ); if ( $this ->checkAccess) { call_user_func( $this ->checkAccess, $this ->id, $model ); } $model ->message = $message = unserialize( $model ->message); if ( empty ( $message [ 'userId' ])) { $message [ 'userId' ] = '0' ; } $model ->message = $message ; $response = Yii:: $app ->response; $response ->formatters = [ yii\web\Response::FORMAT_JSON => [ 'class' => 'yii\web\JsonResponseFormatter' , 'encodeOptions' => 336, ], yii\web\Response::FORMAT_XML => [ 'class' => 'yii\web\XmlResponseFormatter' , ], ]; return [ 'code' => 10000, 'message' => Yii::t( 'success' , '10802' ), 'data' => $model ]; } } |
48、在 Postman 中,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/logs/3, 如图16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | { "code": 10000, "message": "获取日志详情成功", "data": { "id": 3, "level": 4, "category": "api\\behaviors\\RequestLogBehavior::beforeRequest", "log_time": 1529564924.6648, "prefix": "[app-api][/v1/users][]", "message": { "url": "/v1/users", "requestQueryParams": {}, "requestBodyParams": { "email": "111111@163.com", "password": "111111", "username": "111111" }, "userId": "0", "$_SERVER": { "HTTP_ACCEPT_LANGUAGE": "en-US", "HTTP_ACCEPT": "application/json; version=0.0", "HTTP_HOST": "api.github-shuijingwan-yii2-app-advanced.localhost", "REMOTE_ADDR": "127.0.0.1", "REQUEST_URI": "/v1/users", "REQUEST_METHOD": "POST", "CONTENT_TYPE": "application/x-www-form-urlencoded" } } } } |
1 | SELECT * FROM `log` WHERE `id`= '3' |
49、定义请求日志行为,触发 [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] 事件时,写入日志至数据库,编辑 \api\behaviors\RequestLogBehavior.php,将 null 替换为 ”,以保持字段格式统一。
50、接口应用的配置文件,\api\config\main.php,设置日志目标为 DbTarget,设置白名单分类为 api\behaviors\RequestLogBehavior::afterRequest 编辑代码,避免 message[‘userId’] 无法获取值的情况,注:会导致404等请求无法写入日志
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 'log' => [ 'traceLevel' => YII_DEBUG ? 3 : 0, 'targets' => [ 'file' => [ 'class' => 'yii\log\FileTarget' , 'levels' => [ 'error' , 'warning' ], ], 'db' => [ 'class' => 'yii\log\DbTarget' , 'categories' => [ 'api\behaviors\RequestLogBehavior::afterRequest' , ], 'prefix' => function () { $url = !Yii:: $app ->request->isConsoleRequest ? Yii:: $app ->request->getUrl() : null; $user = Yii:: $app ->has( 'user' , true) ? Yii:: $app ->get( 'user' ) : null; $userId = $user ? $user ->getId(false) : '-' ; return sprintf( '[%s][%s][%s]' , Yii:: $app ->id, $url , $userId ); }, 'logVars' => [], ] ], |
51、定义请求日志行为,触发 [[yii\base\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] 事件时,写入日志至数据库,编辑 \api\behaviors\RequestLogBehavior.php,且将字段调整为小写,将其他文件中使用对应字段处,同步修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <?php namespace api\behaviors; use Yii; use yii\base\Behavior; /** * Class RequestLogBehavior * @package api\behaviors * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class RequestLogBehavior extends Behavior { public function events() { return [ Yii:: $app ::EVENT_AFTER_REQUEST => 'afterRequest' , ]; } /** * @inheritdoc */ public function afterRequest( $event ) { $url = !Yii:: $app ->request->isConsoleRequest ? Yii:: $app ->request->getUrl() : '' ; $requestQueryParams = Yii:: $app ->getRequest()->getQueryParams(); $requestBodyParams = Yii:: $app ->getRequest()->getBodyParams(); $user = Yii:: $app ->has( 'user' , true) ? Yii:: $app ->get( 'user' ) : null; $userId = $user ? $user ->getId(false) : '-' ; $message = [ 'url' => $url , 'request_query_params' => $requestQueryParams , 'request_body_params' => $requestBodyParams , 'user_id' => $userId , '$_SERVER' => [ 'HTTP_ACCEPT_LANGUAGE' => $_SERVER [ 'HTTP_ACCEPT_LANGUAGE' ], 'HTTP_ACCEPT' => $_SERVER [ 'HTTP_ACCEPT' ], 'HTTP_HOST' => $_SERVER [ 'HTTP_HOST' ], 'REMOTE_ADDR' => $_SERVER [ 'REMOTE_ADDR' ], 'REQUEST_URI' => $_SERVER [ 'REQUEST_URI' ], 'REQUEST_METHOD' => $_SERVER [ 'REQUEST_METHOD' ], 'CONTENT_TYPE' => $_SERVER [ 'CONTENT_TYPE' ], ], ]; Yii::info(serialize( $message ), __METHOD__ ); } } |
52、在 Postman 中,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/logs?page=77&per-page=3 ,200响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | { "code": 10000, "message": "获取日志列表成功", "data": { "items": [ { "id": 229, "level": 4, "category": "api\\behaviors\\RequestLogBehavior::afterRequest", "log_time": 1529980262.0921, "prefix": "[app-api][/v1/logs?page=12&per-page=76][]", "message": { "url": "/v1/logs?page=12&per-page=76", "request_query_params": { "page": "12", "per-page": "76" }, "request_body_params": {}, "user_id": "0", "$_SERVER": { "HTTP_ACCEPT_LANGUAGE": "zh-CN", "HTTP_ACCEPT": "application/json; version=0.0", "HTTP_HOST": "api.github-shuijingwan-yii2-app-advanced.localhost", "REMOTE_ADDR": "127.0.0.1", "REQUEST_URI": "/v1/logs?page=12&per-page=76", "REQUEST_METHOD": "GET", "CONTENT_TYPE": "" } } }, { "id": 230, "level": 4, "category": "api\\behaviors\\RequestLogBehavior::afterRequest", "log_time": 1529980273.5208, "prefix": "[app-api][/v1/logs?page=76&per-page=3][]", "message": { "url": "/v1/logs?page=76&per-page=3", "request_query_params": { "page": "76", "per-page": "3" }, "request_body_params": {}, "user_id": "0", "$_SERVER": { "HTTP_ACCEPT_LANGUAGE": "zh-CN", "HTTP_ACCEPT": "application/json; version=0.0", "HTTP_HOST": "api.github-shuijingwan-yii2-app-advanced.localhost", "REMOTE_ADDR": "127.0.0.1", "REQUEST_URI": "/v1/logs?page=76&per-page=3", "REQUEST_METHOD": "GET", "CONTENT_TYPE": "" } } } ], "_links": { "self": { }, "first": { }, "prev": { } }, "_meta": { "totalCount": 230, "pageCount": 77, "currentPage": 77, "perPage": 3 } } } |
近期评论