基于 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
<?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'], ], ]; } }
注:如果仅支持较少的行为,可以选择下面的方案,例
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
<?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
'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
<?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
<?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
'modules' => [ 'v1' => [ 'class' => api\modules\v1\Module::class, ], ],
10、在 Postman 中,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1 ,404响应,格式为HMTL,如图7
<!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
'response' => [ 'format' => yii\web\Response::FORMAT_JSON, ],
12、在 Postman 中,GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1 ,404响应,格式为JSON,如图8
{ "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 错误而不是跳转到登录界面.
'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,调整命名空间、继承关系、查询条件等
<?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”)等
<?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 应用组件
'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(简体中文、响应成功)
<?php return [ 10000 => 'success', 10001 => '获取用户列表成功', 10002 => '获取用户详情成功', 10003 => '创建用户成功', 10004 => '更新用户成功', 10005 => '删除用户成功', ];
19、新建语言包文件:\api\messages\zh-CN\error.php(简体中文、响应失败)
<?php return [ 20000 => 'error', 20001 => '用户列表为空', 20002 => '用户ID:{id},不存在', 20003 => '用户ID:{id},的状态为已删除', 20004 => '数据验证失败:{firstErrors}', ];
20、新建语言包文件:\api\messages\en-US\success.php(英语美国、响应成功)
<?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(英语美国、响应失败)
<?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]] 第一个配置项。
'bootstrap' => ['contentNegotiator'], 'components' => [ 'contentNegotiator' => [ 'class' => 'yii\filters\ContentNegotiator', 'languages' => [ 'zh-CN', 'en-US', ], ], ],
删除
'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
{ "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": { "href": "http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users?user=1" } }, "_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
{ "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": { "href": "http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users?user=1" } }, "_meta": { "totalCount": 4, "userCount": 1, "currentuser": 1, "peruser": 20 } } }
25、GET /users/1: 返回用户 1 的详细信息,编辑 \api\rests\user\Action.php,调整命名空间、继承关系、响应结构等
<?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,调整命名空间、继承关系、响应结构等
<?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
{ "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
{ "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
<?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
<?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
<?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
{ "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
{ "code": 20004, "message": "数据验证失败:Username的值\"555555\"已经被占用了。" }
34、PUT /users/5: 更新一个用户,新建 \api\models\UserUpdate.php
<?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
<?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
<?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
{ "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 的值已经被另一用户占用(如果值未发生变化,则可验证通过)
{ "code": 20004, "message": "Data validation failed: Email \"444444@163.com\" has already been taken." }
39、DELETE /users/5: 删除用户5,编辑 \api\rests\user\DeleteAction.php
<?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
{ "code": 10000, "message": "删除用户成功" }
41、编辑 \api\rests\user\OptionsAction.php
<?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
删除
'errorHandler' => [ 'errorAction' => 'site/error', ],
45、总结:现在支持的行为:index、view、create、update、delete、options,皆是继承之后,再次覆写实现具体的需求。
近期评论