基于 yiisoft/yii2-app-advanced,在 GitHub 上新建仓库 yii2-app-advanced,新建接口应用(实现 RESTful 风格的 Web Service 服务的 API),实现 RESTful Web 服务,支持国际化(动态地设置目标语言,默认为简体中文) (三)
1、RESTful Web 服务,建议基于一个单独的接口应用来实现,此时基于 api 应用来实现
2、新建目录:\api\rests,此目录将做为 RESTful Web 服务的操作方法类目录
3、新建控制器类 \api\controllers\UserController.php ,控制器类扩展自 [[yii\rest\ActiveController]]。数据序列化的实现,在响应主体内包含分页信息来简化客户端的开发工作, 如图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 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 | <?php /** * Created by PhpStorm. * User: WangQiang * Date: 2018/04/04 * Time: 15:35 */ namespace api\controllers; use yii\rest\ActiveController; class UserController extends ActiveController { public $serializer = [ 'class' => 'api\rests\user\Serializer' , 'collectionEnvelope' => 'items' , ]; /** * @inheritdoc */ public function actions() { return [ 'index' => [ 'class' => 'api\rests\user\IndexAction' , 'modelClass' => $this ->modelClass, 'checkAccess' => [ $this , 'checkAccess' ], ], 'view' => [ 'class' => 'api\rests\user\ViewAction' , 'modelClass' => $this ->modelClass, 'checkAccess' => [ $this , 'checkAccess' ], ], 'create' => [ 'class' => 'api\rests\user\CreateAction' , 'modelClass' => $this ->modelClass, 'checkAccess' => [ $this , 'checkAccess' ], 'scenario' => $this ->createScenario, ], 'update' => [ 'class' => 'api\rests\user\UpdateAction' , 'modelClass' => $this ->modelClass, 'checkAccess' => [ $this , 'checkAccess' ], 'scenario' => $this ->updateScenario, ], 'delete' => [ 'class' => 'api\rests\user\DeleteAction' , 'modelClass' => $this ->modelClass, 'checkAccess' => [ $this , 'checkAccess' ], ], 'options' => [ 'class' => 'api\rests\user\OptionsAction' , 'modelClass' => $this ->modelClass, 'checkAccess' => [ $this , 'checkAccess' ], ], ]; } } |
注:如果仅支持较少的行为,可以选择下面的方案,例
1 2 3 4 5 6 7 8 | public function actions() { $actions = parent::actions(); $actions [ 'view' ][ 'class' ] = 'api\rests\user\ViewAction' ; return $actions ; } |
4、当配置的内容十分复杂,通用做法是将其存储在一或多个 PHP 文件中, 这些文件被称为配置文件。一个配置文件返回的是 PHP 数组。版本化的实现,配置URL规则,修改有关在应用程序配置的urlManager组件的配置,支持 v1 模块,支持所有行为,新建:\api\config\urlManager.php,如图2
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php return [ 'class' => yii\web\UrlManager:: class , 'enablePrettyUrl' => true, 'enableStrictParsing' => true, 'showScriptName' => false, 'rules' => [ [ 'class' => 'yii\rest\UrlRule' , 'controller' => [ 'v1/user' ], ], ], ]; |
5、将 \api\config\urlManager.php 包含在 \api\config\main.php 里,编辑 \api\config\main.php
1 | 'urlManager' => require __DIR__ . '/urlManager.php' , |
6、把每个主要版本的 API 实现在一个单独的模块 ID 的主版本号,基于 Gii 生成模块 v1,打开网址:http://www.github-shuijingwan-yii2-app-advanced.localhost/gii/module ,删除目录:\api\modules\v1\views,如图3
7、新建 \api\modules\v1\models\User.php,继承至 \api\models\User.php,如图4
注:\api\modules\v1\models\User(仅用于 v1 模块) > \api\models\User(仅用于 api 应用) > \common\logics\User.php(可用于 api、frontend 等多个应用) > \common\models\User.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/04/04 * Time: 16:04 */ namespace api\modules\v1\models; class User extends \api\models\User { } |
8、\api\modules\v1\controllers\DefaultController.php 重命名为 \api\modules\v1\controllers\UserController.php,通过指定 [[yii\rest\ActiveController::modelClass|modelClass]] 作为 api\modules\v1\models\User, 控制器就能知道使用哪个模型去获取和处理数据。编辑代码,如图5
注:\api\modules\v1\controllers\UserController.php(仅用于 v1 模块) > \api\controllers\UserController.php(仅用于 api 应用) > \yii\rest\ActiveController
1 2 3 4 5 6 7 8 9 10 11 | <?php namespace api\modules\v1\controllers; /** * User controller for the `v1` module */ class UserController extends \api\controllers\UserController { public $modelClass = 'api\modules\v1\models\User' ; } |
9、要在应用中使用模块,只需要将模块加入到应用主体配置的[[yii\base\Application::modules|modules]]属性的列表中, 如下代码的应用主体配置 使用 v1 模块,编辑 \api\config\main.php,如图6
1 2 3 4 5 | 'modules' => [ 'v1' => [ 'class' => api\modules\v1\Module:: class , ], ], |
10、在 Postman 中,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1 ,404响应,格式为HMTL,如图7
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <! DOCTYPE html> < html lang = "zh-CN" > < head > < meta charset = "UTF-8" > < meta http-equiv = "X-UA-Compatible" content = "IE=edge" > < meta name = "viewport" content = "width=device-width, initial-scale=1" > < meta name = "csrf-param" content = "_csrf-api" > < meta name = "csrf-token" content = "00ssponpmJwoWBZLFTExhai8kSgeTCkNzKenlyRbznyiAHzyz6Pc9XICYxpmWV7KzePdQnYZG2umkezQXg38Tw==" > < title >Not Found (#404)</ title > < link href = "/assets/73866dfd/css/bootstrap.css" rel = "stylesheet" > < link href = "/css/site.css" rel = "stylesheet" > </ head > < body > </ body > </ html > |
11、对于404响应格式为HTML的解决,编辑 \api\config\main.php,设置默认的响应格式为JSON
1 2 3 | 'response' => [ 'format' => yii\web\Response::FORMAT_JSON, ], |
12、在 Postman 中,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1 ,404响应,格式为JSON,如图8
1 2 3 4 5 6 7 | { "name": "Not Found", "message": "用户未找到。", "code": 0, "status": 404, "type": "yii\\web\\NotFoundHttpException" } |
13、RESTful APIs 通常是无状态的,因此,配置 user 应用组件,编辑 \api\config\main.php
注:配置user 应用组件:
设置 [[yii\web\User::enableSession|enableSession]] 属性为 false.
设置 [[yii\web\User::loginUrl|loginUrl]] 属性为null 显示一个HTTP 403 错误而不是跳转到登录界面.
1 2 3 4 5 6 | 'user' => [ 'identityClass' => 'api\models\User' , 'enableSession' => false, 'loginUrl' => null, 'enableAutoLogin' => false, ], |
14、复制目录 \vendor\yiisoft\yii2\rest 下的 Action.php、IndexAction.php、OptionsAction.php、ViewAction.php、CreateAction.php、UpdateAction.php、DeleteAction.php、Serializer.php 至目录 \api\rests\user,如果为多个单词组合的目录,建议目录使用小写+下划线,参考网址:https://github.com/hfcorriez/fig-standards/blob/master/accepted/zh_CN/PSR-0.md
15、编辑 \api\rests\user\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 | <?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace api\rests\user; 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 { const STATUS_DELETED = 0; //状态:已删除 const STATUS_ACTIVE = 10; //状态:活跃 /** * 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) { return $this ->dataFilter; } } } if ( $this ->prepareDataProvider !== null) { return call_user_func( $this ->prepareDataProvider, $this , $filter ); } /* @var $modelClass \yii\db\BaseActiveRecord */ $modelClass = $this ->modelClass; $query = $modelClass ::find()->where([ 'status' => self::STATUS_ACTIVE]); if (! empty ( $filter )) { $query ->andWhere( $filter ); } return Yii::createObject([ 'class' => ActiveDataProvider::className(), 'query' => $query , 'pagination' => [ 'params' => $requestParams , ], 'sort' => [ 'params' => $requestParams , ], ]); } } |
16、编辑 \api\rests\user\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 | <?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace api\rests\user; 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' => 20001, 'message' => Yii::t( 'error' , '20001' )]; } if ( $pagination !== false) { return [ 'code' => 10000, 'message' => Yii::t( 'success' , '10001' ), 'data' => array_merge ( $result , $this ->serializePagination( $pagination ))]; } return [ 'code' => 10000, 'message' => Yii::t( 'success' , '10001' ), 'data' => $result ]; } } |
17、编辑 \api\config\main.php,配置接口应用的 i18n 应用组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 'i18n' => [ 'translations' => [ 'model/*' => [ 'class' => 'yii\i18n\PhpMessageSource' , 'forceTranslation' => true, 'basePath' => '@common/messages' , 'fileMap' =>[ ], ], '*' => [ 'class' => 'yii\i18n\PhpMessageSource' , 'forceTranslation' => true, 'basePath' => '@api/messages' , 'fileMap' =>[ ], ], ], ], |
18、新建语言包文件:\api\messages\zh-CN\success.php(简体中文、响应成功)
1 2 3 4 5 6 7 8 9 | <?php return [ 10000 => 'success' , 10001 => '获取用户列表成功' , 10002 => '获取用户详情成功' , 10003 => '创建用户成功' , 10004 => '更新用户成功' , 10005 => '删除用户成功' , ]; |
19、新建语言包文件:\api\messages\zh-CN\error.php(简体中文、响应失败)
1 2 3 4 5 6 7 8 | <?php return [ 20000 => 'error' , 20001 => '用户列表为空' , 20002 => '用户ID:{id},不存在' , 20003 => '用户ID:{id},的状态为已删除' , 20004 => '数据验证失败:{firstErrors}' , ]; |
20、新建语言包文件:\api\messages\en-US\success.php(英语美国、响应成功)
1 2 3 4 5 6 7 8 9 | <?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' , ]; |
21、新建语言包文件:\api\messages\en-US\error.php(英语美国、响应失败)
1 2 3 4 5 6 7 8 | <?php return [ 20000 => 'error' , 20001 => 'User list is empty' , 20002 => 'User ID: {id}, does not exist' , 20003 => 'User ID: {id}, the status is not active' , 20004 => 'Data validation failed: {firstErrors}' , ]; |
22、ContentNegotiator支持响应内容格式处理和语言处理。 通过检查 GET 参数和 Accept HTTP头部来决定响应内容格式和语言。配置ContentNegotiator支持英语美国和简体中文。编辑 \common\config\main.php
注:如果请求中没有检测到语言, 使用 [[languages]] 第一个配置项。
1 2 3 4 5 6 7 8 9 10 11 12 | 'bootstrap' => [ 'contentNegotiator' ], 'components' => [ 'contentNegotiator' => [ 'class' => 'yii\filters\ContentNegotiator' , 'languages' => [ 'zh-CN' , 'en-US' , ], ], ], |
删除
1 2 | 'sourceLanguage' => 'en-US' , 'language' => 'zh-CN' , |
23、在 Postman 中,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users ,200响应,message默认为简体中文,如图9
注:
Accept application/json; version=0.0
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 | { "code": 10000, "message": "获取用户列表成功", "data": { "items": [ { "id": 1, "username": "111111", "auth_key": "P66wcJVGQL2toEpYr-Kc3z_rk7jJHMip", "password_hash": "$2y$13$qWV4xv1YLBtyn5cVx106VO6kTUOUP/7kX3oaC6CVpczHobkUrc6AG", "password_reset_token": null, "email": "111111@163.com", "status": 10, "created_at": 1522821105, "updated_at": 1522821105 }, { "id": 2, "username": "222222", "auth_key": "MgGfnpw6mYtkzctYkTOscyJAY_xxu739", "password_hash": "$2y$13$Xe0I4yjXL1FLvue8Q1YEU.nX8wH9HZzBOHSdbx2FL49me15CqoSa2", "password_reset_token": null, "email": "222222@163.com", "status": 10, "created_at": 1522821319, "updated_at": 1522821319 }, { "id": 3, "username": "333333", "auth_key": "WPeS81Lh4EUg0dp8FQ7ely4MF1ucF-ph", "password_hash": "$2y$13$.2G63zy8q2wTH/dWf.gLVe7c8kXKw3YCT5gquVhNhpTS2Albro1Ua", "password_reset_token": null, "email": "333333@163.com", "status": 10, "created_at": 1522823787, "updated_at": 1522823787 }, { "id": 4, "username": "444444", "auth_key": "scn4skhTfkg9YvRH1q9JZHkkddSR9H6a", "password_hash": "$2y$13$0RNx33MZIwC.FlSzMB0fuOCJAKmb1TlZzWCAujaCY/ixiM11myQ0a", "password_reset_token": null, "email": "444444@163.com", "status": 10, "created_at": 1522823915, "updated_at": 1522823915 } ], "_links": { "self": { } }, "_meta": { "totalCount": 4, "userCount": 1, "currentuser": 1, "peruser": 20 } } } |
24、在 Postman 中,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users ,200响应,message为简体中文,如图10
注:
Accept application/json; version=0.0
Accept-Language zh-CN
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 | { "code": 10000, "message": "获取用户列表成功", "data": { "items": [ { "id": 1, "username": "111111", "auth_key": "P66wcJVGQL2toEpYr-Kc3z_rk7jJHMip", "password_hash": "$2y$13$qWV4xv1YLBtyn5cVx106VO6kTUOUP/7kX3oaC6CVpczHobkUrc6AG", "password_reset_token": null, "email": "111111@163.com", "status": 10, "created_at": 1522821105, "updated_at": 1522821105 }, { "id": 2, "username": "222222", "auth_key": "MgGfnpw6mYtkzctYkTOscyJAY_xxu739", "password_hash": "$2y$13$Xe0I4yjXL1FLvue8Q1YEU.nX8wH9HZzBOHSdbx2FL49me15CqoSa2", "password_reset_token": null, "email": "222222@163.com", "status": 10, "created_at": 1522821319, "updated_at": 1522821319 }, { "id": 3, "username": "333333", "auth_key": "WPeS81Lh4EUg0dp8FQ7ely4MF1ucF-ph", "password_hash": "$2y$13$.2G63zy8q2wTH/dWf.gLVe7c8kXKw3YCT5gquVhNhpTS2Albro1Ua", "password_reset_token": null, "email": "333333@163.com", "status": 10, "created_at": 1522823787, "updated_at": 1522823787 }, { "id": 4, "username": "444444", "auth_key": "scn4skhTfkg9YvRH1q9JZHkkddSR9H6a", "password_hash": "$2y$13$0RNx33MZIwC.FlSzMB0fuOCJAKmb1TlZzWCAujaCY/ixiM11myQ0a", "password_reset_token": null, "email": "444444@163.com", "status": 10, "created_at": 1522823915, "updated_at": 1522823915 } ], "_links": { "self": { } }, "_meta": { "totalCount": 4, "userCount": 1, "currentuser": 1, "peruser": 20 } } } |
25、GET /users/1: 返回用户 1 的详细信息,编辑 \api\rests\user\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\user; 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' , '20002' ), [ 'id' => $id ])), 20002); } } |
26、编辑 \api\rests\user\ViewAction.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 | <?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace api\rests\user; 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 { const STATUS_DELETED = 0; //状态:已删除 const STATUS_ACTIVE = 10; //状态:活跃 /** * 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 ); } /* 判断状态,如果为已删除,则返回失败 */ if ( $model ->status === self::STATUS_DELETED) { return [ 'code' => 20003, 'message' => Yii::t( 'error' , Yii::t( 'error' , Yii::t( 'error' , '20003' ), [ 'id' => $id ]))]; } return [ 'code' => 10000, 'message' => Yii::t( 'success' , '10002' ), 'data' => $model ]; } } |
27、在 Postman 中,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/1 ,200响应,其状态为活跃,如图11
注:
Accept application/json; version=0.0
Accept-Language zh-CN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | { "code": 10000, "message": "获取用户详情成功", "data": { "id": 1, "username": "111111", "auth_key": "P66wcJVGQL2toEpYr-Kc3z_rk7jJHMip", "password_hash": "$2y$13$qWV4xv1YLBtyn5cVx106VO6kTUOUP/7kX3oaC6CVpczHobkUrc6AG", "password_reset_token": null, "email": "111111@163.com", "status": 10, "created_at": 1522821105, "updated_at": 1522821105 } } |
28、在 Postman 中,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/5 ,404响应,用户不存在,如图12
注:
Accept application/json; version=0.0
Accept-Language en-US
1 2 3 4 5 6 7 | { "name": "Not Found", "message": "User ID: 5, does not exist", "code": 20002, "status": 404, "type": "yii\\web\\NotFoundHttpException" } |
29、POST /users: 创建一个新用户,复制 \api\models\SignupForm.php 至 \api\models\Signup.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 | <?php namespace api\models; use yii\base\Model; /** * Signup */ class Signup extends Model { public $username ; public $email ; public $password ; /** * {@inheritdoc} */ public function rules() { return [ [ 'username' , 'trim' ], [ 'username' , 'required' ], [ 'username' , 'unique' , 'targetClass' => '\api\models\User' ], [ 'username' , 'string' , 'min' => 2, 'max' => 255], [ 'email' , 'trim' ], [ 'email' , 'required' ], [ 'email' , 'email' ], [ 'email' , 'string' , 'max' => 255], [ 'email' , 'unique' , 'targetClass' => '\api\models\User' ], [ 'password' , 'required' ], [ 'password' , 'string' , 'min' => 6], ]; } } |
30、新建 \api\modules\v1\models\Signup.php,继承至 \api\models\Signup.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php /** * Created by PhpStorm. * User: WangQiang * Date: 2018/04/08 * Time: 10:48 */ namespace api\modules\v1\models; class Signup extends \api\models\Signup { } |
31、POST /users: 创建一个新用户,编辑 \api\rests\user\CreateAction.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 85 86 87 88 89 90 91 | <?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace api\rests\user; use Yii; use yii\base\Model; use api\models\Signup; use yii\helpers\Url; use yii\web\ServerErrorHttpException; /** * CreateAction implements the API endpoint for creating a new model from the given data. * * For more details and usage information on CreateAction, see the [guide article on rest controllers](guide:rest-controllers). * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class CreateAction extends Action { /** * @var string the scenario to be assigned to the new model before it is validated and saved. */ public $scenario = Model::SCENARIO_DEFAULT; /** * @var string the name of the view action. This property is need to create the URL when the model is successfully created. */ public $viewAction = 'view' ; /** * Creates a new model. * @return \yii\db\ActiveRecordInterface the model newly created * @throws ServerErrorHttpException if there is any error when creating the model */ public function run() { if ( $this ->checkAccess) { call_user_func( $this ->checkAccess, $this ->id); } $signup = new Signup(); $signup ->load(Yii:: $app ->getRequest()->getBodyParams(), '' ); if (! $signup ->validate()) { if ( $signup ->hasErrors()) { $response = Yii:: $app ->getResponse(); $response ->setStatusCode(422, 'Data Validation Failed.' ); foreach ( $signup ->getFirstErrors() as $message ) { $firstErrors = $message ; break ; } return [ 'code' => 20004, 'message' => Yii::t( 'error' , Yii::t( 'error' , Yii::t( 'error' , '20004' ), [ 'firstErrors' => $firstErrors ]))]; } elseif (! $signup ->hasErrors()) { throw new ServerErrorHttpException( 'Failed to create the object for unknown reason.' ); } } /* @var $model \yii\db\ActiveRecord */ $model = new $this ->modelClass([ 'scenario' => $this ->scenario, ]); $model ->username = $signup ->username; $model ->email = $signup ->email; $model ->setPassword( $signup ->password); $model ->generateAuthKey(); if ( $model ->save()) { $response = Yii:: $app ->getResponse(); $response ->setStatusCode(201); $id = implode( ',' , array_values ( $model ->getPrimaryKey(true))); $response ->getHeaders()->set( 'Location' , Url::toRoute([ $this ->viewAction, 'id' => $id ], true)); } elseif ( $model ->hasErrors()) { $response = Yii:: $app ->getResponse(); $response ->setStatusCode(422, 'Data Validation Failed.' ); foreach ( $model ->getFirstErrors() as $message ) { $firstErrors = $message ; break ; } return [ 'code' => 20004, 'message' => Yii::t( 'error' , Yii::t( 'error' , Yii::t( 'error' , '20004' ), [ 'firstErrors' => $firstErrors ]))]; } elseif (! $model ->hasErrors()) { throw new ServerErrorHttpException( 'Failed to create the object for unknown reason.' ); } return [ 'code' => 10000, 'message' => Yii::t( 'success' , '10003' ), 'data' => $model ]; } } |
32、在 Postman 中,POST http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users ,201响应,如图13
注:
Accept application/json; version=0.0
Accept-Language zh-CN
Accept-Encoding gzip, deflate, br
Content-Type application/x-www-form-urlencoded
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | { "code": 10000, "message": "创建用户成功", "data": { "username": "555555", "email": "555555@163.com", "password_hash": "$2y$13$JcBRxuLFtLc4FfACRGwKpeXRNfhuP8XRGR/0ORvpXc8ob3c3D2E/q", "auth_key": "0eAriaA02Qw9J3s8ngwLtGlW34tpZ98k", "status": 10, "created_at": 1523156052, "updated_at": 1523156052, "id": 5 } } |
33、在 Postman 中,POST http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users ,参数保持原样,422响应,如图14
注:
Accept application/json; version=0.0
Accept-Language zh-CN
Accept-Encoding gzip, deflate, br
Content-Type application/x-www-form-urlencoded
1 2 3 4 | { "code": 20004, "message": "数据验证失败:Username的值\"555555\"已经被占用了。" } |
34、PUT /users/5: 更新一个用户,新建 \api\models\UserUpdate.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 | <?php namespace api\models; use yii\base\Model; /** * UserUpdate */ class UserUpdate extends Model { public $id ; public $email ; public $password ; public $status ; const STATUS_DELETED = 0; //状态:已删除 const STATUS_ACTIVE = 10; //状态:活跃 /** * {@inheritdoc} */ public function rules() { return [ [ 'email' , 'trim' ], [ 'email' , 'required' ], [ 'email' , 'email' ], [ 'email' , 'string' , 'max' => 255], [ 'email' , 'unique' , 'targetClass' => '\api\models\User' , 'filter' => [ '!=' , 'id' , $this ->id]], [ 'password' , 'required' ], [ 'password' , 'string' , 'min' => 6], [[ 'status' ], 'in' , 'range' => [self::STATUS_DELETED, self::STATUS_ACTIVE]], ]; } } |
35、新建 \api\modules\v1\models\UserUpdate.php,继承至 \api\models\UserUpdate.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php /** * Created by PhpStorm. * User: WangQiang * Date: 2018/04/08 * Time: 11:19 */ namespace api\modules\v1\models; class UserUpdate extends \api\models\UserUpdate { } |
36、PUT /users/4: 更新一个用户,编辑 \api\rests\user\UpdateAction.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 85 | <?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace api\rests\user; use Yii; use yii\base\Model; use yii\db\ActiveRecord; use api\models\UserUpdate; use yii\web\ServerErrorHttpException; /** * UpdateAction implements the API endpoint for updating a model. * * For more details and usage information on UpdateAction, see the [guide article on rest controllers](guide:rest-controllers). * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class UpdateAction extends Action { /** * @var string the scenario to be assigned to the model before it is validated and updated. */ public $scenario = Model::SCENARIO_DEFAULT; /** * Updates an existing model. * @param string $id the primary key of the model. * @return \yii\db\ActiveRecordInterface the model being updated * @throws ServerErrorHttpException if there is any error when updating the model */ public function run( $id ) { /* @var $model ActiveRecord */ $model = $this ->findModel( $id ); if ( $this ->checkAccess) { call_user_func( $this ->checkAccess, $this ->id, $model ); } $userUpdate = new UserUpdate(); $userUpdate ->id = $id ; $userUpdate ->load(Yii:: $app ->getRequest()->getBodyParams(), '' ); if (! $userUpdate ->validate()) { if ( $userUpdate ->hasErrors()) { $response = Yii:: $app ->getResponse(); $response ->setStatusCode(422, 'Data Validation Failed.' ); foreach ( $userUpdate ->getFirstErrors() as $message ) { $firstErrors = $message ; break ; } return [ 'code' => 20004, 'message' => Yii::t( 'error' , Yii::t( 'error' , Yii::t( 'error' , '20004' ), [ 'firstErrors' => $firstErrors ]))]; } elseif (! $userUpdate ->hasErrors()) { throw new ServerErrorHttpException( 'Failed to create the object for unknown reason.' ); } } $model ->scenario = $this ->scenario; $model ->email = $userUpdate ->email; $model ->status = $userUpdate ->status; $model ->setPassword( $userUpdate ->password); $model ->generateAuthKey(); if ( $model ->save() === false) { if ( $model ->hasErrors()) { $response = Yii:: $app ->getResponse(); $response ->setStatusCode(422, 'Data Validation Failed.' ); foreach ( $model ->getFirstErrors() as $message ) { $firstErrors = $message ; break ; } return [ 'code' => 20004, 'message' => Yii::t( 'error' , Yii::t( 'error' , Yii::t( 'error' , '20004' ), [ 'firstErrors' => $firstErrors ]))]; } elseif (! $model ->hasErrors()) { throw new ServerErrorHttpException( 'Failed to update the object for unknown reason.' ); } } return [ 'code' => 10000, 'message' => Yii::t( 'success' , '10004' ), 'data' => $model ]; } } |
37、在 Postman 中,PUT http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/5 ,200响应,如图15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | { "code": 10000, "message": "更新用户成功", "data": { "id": 5, "username": "555555", "auth_key": "nXryM-o_ky4TAdn758l47pQhLhsSnjSZ", "password_hash": "$2y$13$qB5Y3Zl7B8NyPDFMXc06ye8OAhybCpp7p6wToyhtLH2iDzhd06ts.", "password_reset_token": null, "email": "555555@qq.com", "status": "0", "created_at": 1523156052, "updated_at": 1523165956 } } |
38、在 Postman 中,PUT http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/4 ,422响应,如图16
注:
Accept application/json; version=0.0
Accept-Language en-US
Accept-Encoding gzip, deflate, br
Content-Type application/x-www-form-urlencoded
Body
email 的值已经被另一用户占用(如果值未发生变化,则可验证通过)
1 2 3 4 | { "code": 20004, "message": "Data validation failed: Email \"444444@163.com\" has already been taken." } |
39、DELETE /users/5: 删除用户5,编辑 \api\rests\user\DeleteAction.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 | <?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace api\rests\user; use Yii; use yii\web\ServerErrorHttpException; /** * DeleteAction implements the API endpoint for deleting a model. * * For more details and usage information on DeleteAction, see the [guide article on rest controllers](guide:rest-controllers). * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class DeleteAction extends Action { /** * Deletes a model. * @param mixed $id id of the model to be deleted. * @throws ServerErrorHttpException on failure. */ public function run( $id ) { $model = $this ->findModel( $id ); if ( $this ->checkAccess) { call_user_func( $this ->checkAccess, $this ->id, $model ); } if ( $model -> delete () === false) { throw new ServerErrorHttpException( 'Failed to delete the object for unknown reason.' ); } return [ 'code' => 10000, 'message' => Yii::t( 'success' , '10005' )]; } } |
40、在 Postman 中,DELETE http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/5 ,200响应,如图17
注:
Accept application/json; version=0.0
Accept-Language zh-CN
Accept-Encoding gzip, deflate, br
1 2 3 4 | { "code": 10000, "message": "删除用户成功" } |
41、编辑 \api\rests\user\OptionsAction.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 | <?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace api\rests\user; use Yii; /** * OptionsAction responds to the OPTIONS request by sending back an `Allow` header. * * For more details and usage information on OptionsAction, see the [guide article on rest controllers](guide:rest-controllers). * * @author Qiang Xue <qiang.xue@gmail.com> * @since 2.0 */ class OptionsAction extends \yii\rest\Action { /** * @var array the HTTP verbs that are supported by the collection URL */ public $collectionOptions = [ 'GET' , 'POST' , 'HEAD' , 'OPTIONS' ]; /** * @var array the HTTP verbs that are supported by the resource URL */ public $resourceOptions = [ 'GET' , 'PUT' , 'PATCH' , 'DELETE' , 'HEAD' , 'OPTIONS' ]; /** * Responds to the OPTIONS request. * @param string $id */ public function run( $id = null) { if (Yii:: $app ->getRequest()->getMethod() !== 'OPTIONS' ) { Yii:: $app ->getResponse()->setStatusCode(405); } $options = $id === null ? $this ->collectionOptions : $this ->resourceOptions; $headers = Yii:: $app ->getResponse()->getHeaders(); $headers ->set( 'Allow' , implode( ', ' , $options )); $headers ->set( 'Access-Control-Allow-Methods' , implode( ', ' , $options )); } } |
42、OPTIONS /users: 显示关于末端 /users 支持的动词,在 Postman 中,OPTIONS http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users ,200响应,如图18
43、OPTIONS /users/4: 显示关于末端 /users/4 支持的动词,在 Postman 中,OPTIONS http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/1 ,200响应,如图19
44、由于[[yii\web\Response::format|response format]] 响应格式不为html, 因此,不会使用错误或异常视图来显示错误信息,可取消错误动作,编辑 \api\config\main.php
删除
1 2 3 | 'errorHandler' => [ 'errorAction' => 'site/error' , ], |
45、总结:现在支持的行为:index、view、create、update、delete、options,皆是继承之后,再次覆写实现具体的需求。
近期评论