1. It is recommended to implement RESTful Web services based on a standalone API application. In this case, it is implemented based on the api application.
2. Create a new directory: \api\rests. This directory will serve as the directory for the action method classes of the RESTful Web service.
3. Create a new controller class \api\controllers\UserController.php. The controller class extends [[yii\rest\ActiveController]]. The implementation of data serialization includes pagination information in the response body to simplify client development, as shown in Figure 1.
![Create a new controller class \api\controllers\UserController.php. The controller class extends [[yii\rest\ActiveController]]. The implementation of data serialization includes pagination information in the response body to simplify client development](https://media.shuijingwanwq.com/2018/04/1-2.png)
<?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'],
],
];
}
}
Note: If only a few actions need to be supported, you can choose the following solution, for example:
public function actions()
{
$actions = parent::actions();
$actions['view']['class'] = 'api\rests\user\ViewAction';
return $actions;
}
4. When the configuration content is very complex, the common practice is to store it in one or more PHP files. These files are called configuration files. A configuration file returns a PHP array. To implement versioning, configure the URL rules by modifying the configuration of the urlManager component in the application configuration to support the v1 module and all actions. Create: \api\config\urlManager.php, as shown in Figure 2.

<?php
return [
'class' => yii\web\UrlManager::class,
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => ['v1/user'],
],
],
];5. Include \api\config\urlManager.php in \api\config\main.php. Edit \api\config\main.php.
'urlManager' => require __DIR__ . '/urlManager.php',
6. Implement each major version of the API in a separate module ID with the major version number. Generate the v1 module using Gii by opening the URL: http://www.github-shuijingwan-yii2-app-advanced.localhost/gii/module. Delete the directory: \api\modules\v1\views, as shown in Figure 3.

7. Create \api\modules\v1\models\User.php, which inherits from \api\models\User.php, as shown in Figure 4.
Note: \api\modules\v1\models\User (only for the v1 module) > \api\models\User (only for the api application) > \common\logics\User.php (can be used by multiple applications such as api, frontend, etc.) > \common\models\User.php (limited to Gii generation) > \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. Rename \api\modules\v1\controllers\DefaultController.php to \api\modules\v1\controllers\UserController.php. By specifying [[yii\rest\ActiveController::modelClass|modelClass]] as api\modules\v1\models\User, the controller knows which model to use for fetching and processing data. Edit the code, as shown in Figure 5.
Note: \api\modules\v1\controllers\UserController.php (only for the v1 module) > \api\controllers\UserController.php (only for the api application) > \yii\rest\ActiveController
![Rename \api\modules\v1\controllers\DefaultController.php to \api\modules\v1\controllers\UserController.php. By specifying [[yii\rest\ActiveController::modelClass|modelClass]] as api\modules\v1\models\User, the controller knows which model to use for fetching and processing data](https://media.shuijingwanwq.com/2018/04/5-2.png)
<?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. To use a module in an application, simply add the module to the list of the [[yii\base\Application::modules|modules]] property in the application configuration. The application configuration in the following code uses the v1 module. Edit \api\config\main.php, as shown in Figure 6.
![To use a module in an application, simply add the module to the list of the [[yii\base\Application::modules|modules]] property in the application configuration. The application configuration in the following code uses the v1 module. Edit \api\config\main.php](https://media.shuijingwanwq.com/2018/04/6-2.png)
'modules' => [
'v1' => [
'class' => api\modules\v1\Module::class,
],
],
10. In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1 returns a 404 response in HTML format, as shown in Figure 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. To resolve the 404 response being in HTML format, edit \api\config\main.php and set the default response format to JSON.
'response' => [
'format' => yii\web\Response::FORMAT_JSON,
],
12. In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1 returns a 404 response in JSON format, as shown in Figure 8.

{
"name": "Not Found",
"message": "用户未找到。",
"code": 0,
"status": 404,
"type": "yii\\web\\NotFoundHttpException"
}
13. RESTful APIs are typically stateless. Therefore, configure the user application component by editing \api\config\main.php.
Note: Configure the user application component:
Set the [[yii\web\User::enableSession|enableSession]] property to false.
Set the [[yii\web\User::loginUrl|loginUrl]] property to null to display an HTTP 403 error instead of redirecting to the login page.
'user' => [
'identityClass' => 'api\models\User',
'enableSession' => false,
'loginUrl' => null,
'enableAutoLogin' => false,
],
14. Copy Action.php, IndexAction.php, OptionsAction.php, ViewAction.php, CreateAction.php, UpdateAction.php, DeleteAction.php, and Serializer.php from the \vendor\yiisoft\yii2\rest directory to the \api\rests\user directory. If the directory name consists of multiple words, it is recommended to use lowercase with underscores. Reference URL: https://github.com/hfcorriez/fig-standards/blob/master/accepted/zh_CN/PSR-0.md
15. Edit \api\rests\user\IndexAction.php to adjust the namespace, inheritance relationship, query conditions, etc.
<?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. Edit \api\rests\user\Serializer.php to adjust the namespace, inheritance relationship, response structure (successful response: “code”: 10000, “message”, “data”; failed response: “code”: a number other than 10000, “message”), etc.
<?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. Edit \api\config\main.php to configure the i18n application component for the API application.
'i18n' => [
'translations' => [
'model/*'=> [
'class' => 'yii\i18n\PhpMessageSource',
'forceTranslation' => true,
'basePath'=>'@common/messages',
'fileMap'=>[
],
],
'*'=> [
'class' => 'yii\i18n\PhpMessageSource',
'forceTranslation' => true,
'basePath'=>'@api/messages',
'fileMap'=>[
],
],
],
],
18. Create a language pack file: \api\messages\zh-CN\success.php (Simplified Chinese, successful response).
<?php
return [
10000 => 'success',
10001 => '获取用户列表成功',
10002 => '获取用户详情成功',
10003 => '创建用户成功',
10004 => '更新用户成功',
10005 => '删除用户成功',
];19. Create a language pack file: \api\messages\zh-CN\error.php (Simplified Chinese, failed response).
<?php
return [
20000 => 'error',
20001 => '用户列表为空',
20002 => '用户ID:{id},不存在',
20003 => '用户ID:{id},的状态为已删除',
20004 => '数据验证失败:{firstErrors}',
];20. Create a language pack file: \api\messages\en-US\success.php (US English, successful response).
<?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. Create a language pack file: \api\messages\en-US\error.php (US English, failed response).
<?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 supports response content format handling and language handling. It determines the response content format and language by inspecting the GET parameters and the Accept HTTP header. Configure ContentNegotiator to support US English and Simplified Chinese. Edit \common\config\main.php.
Note: If no language is detected in the request, the first configured item in [[languages]] is used.
'bootstrap' => ['contentNegotiator'],
'components' => [
'contentNegotiator' => [
'class' => 'yii\filters\ContentNegotiator',
'languages' => [
'zh-CN',
'en-US',
],
],
],
Delete
'sourceLanguage' => 'en-US',
'language' => 'zh-CN',
23. In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users returns a 200 response, with the message defaulting to Simplified Chinese, as shown in Figure 9.
Note:
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. In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users returns a 200 response, with the message in Simplified Chinese, as shown in Figure 10.
Note:
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: Returns the details of user 1. Edit \api\rests\user\Action.php to adjust the namespace, inheritance relationship, response structure, etc.
<?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. Edit \api\rests\user\ViewAction.php to adjust the namespace, inheritance relationship, response structure, etc.
<?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. In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/1 returns a 200 response, and its status is active, as shown in Figure 11.
Note:
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. In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/5 returns a 404 response, indicating the user does not exist, as shown in Figure 12.
Note:
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: Create a new user. Copy \api\models\SignupForm.php to \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. Create \api\modules\v1\models\Signup.php, which inherits from \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: Create a new user. Edit \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. In Postman, POST http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users returns a 201 response, as shown in Figure 13.
Note:
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. In Postman, POST http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users with the parameters kept as is returns a 422 response, as shown in Figure 14.
Note:
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: Update a user. Create \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. Create \api\modules\v1\models\UserUpdate.php, which inherits from \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: Update a user. Edit \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. In Postman, PUT http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/5 returns a 200 response, as shown in Figure 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. In Postman, PUT http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/4 returns a 422 response, as shown in Figure 16.
Note:
Accept application/json; version=0.0
Accept-Language en-US
Accept-Encoding gzip, deflate, br
Content-Type application/x-www-form-urlencoded
Body
The value of email has already been taken by another user (if the value remains unchanged, validation passes).

{
"code": 20004,
"message": "Data validation failed: Email \"444444@163.com\" has already been taken."
}
39. DELETE /users/5: Delete user 5. Edit \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. In Postman, DELETE http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/5 returns a 200 response, as shown in Figure 17.
Note:
Accept application/json; version=0.0
Accept-Language zh-CN
Accept-Encoding gzip, deflate, br

{
"code": 10000,
"message": "删除用户成功"
}
41. Edit \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: Shows the verbs supported by the endpoint /users. In Postman, OPTIONS http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users returns a 200 response, as shown in Figure 18.

43. OPTIONS /users/4: Shows the verbs supported by the endpoint /users/4. In Postman, OPTIONS http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/1 returns a 200 response, as shown in Figure 19.

44. Since the [[yii\web\Response::format|response format]] is not html, error or exception views will not be used to display error messages. The error action can be removed. Edit \api\config\main.php.
Delete
'errorHandler' => [
'errorAction' => 'site/error',
],
45. Summary: The currently supported actions are: index, view, create, update, delete, and options. All of them are implemented by overriding the inherited methods to meet specific requirements.
