1. In Windows PowerShell, navigate to the E:\wwwroot\cmcp-api directory, as shown in Figure 1

2. Create a new api application by copying the backend directory to api, as shown in Figure 2

3. Edit \.env.dist to add api-related configurations; this is the configuration file committed to Git version control, as shown in Figure 3
API_HOST_INFO = http://api.yii2-starter-kit.dev
#API_HOST_INFO = http://yii2-starter-kit.dev
#API_BASE_URL = /api/web
API_COOKIE_VALIDATION_KEY =

4. Edit \.env to add api-related configurations; this is the actual effective configuration file, as shown in Figure 4
API_HOST_INFO = http://www.cmcp-api.localhost
#API_HOST_INFO = http://yii2-starter-kit.dev
#API_BASE_URL = /api/web
API_COOKIE_VALIDATION_KEY =

5. Edit \common\config\bootstrap.php to configure the alias for the API application, as shown in Figure 5
Yii::setAlias('@api', realpath(__DIR__.'/../../api'));
Yii::setAlias('@apiUrl', env('API_HOST_INFO') . env('API_BASE_URL') );

6. Edit \common\config\base.php to configure the language pack file for the API application, as shown in Figure 6
'api'=>'api.php',

7. Create a Chinese language pack file for the API application by copying \common\messages\zh\backend.php to \common\messages\zh\api.php (if you need to support other languages, you can create it in the respective language directory), as shown in Figure 7

8. Edit \common\config\base.php to configure URL parsing and generation for the API application, as shown in Figure 8
'urlManagerApi' => \yii\helpers\ArrayHelper::merge(
[
'hostInfo' => env('API_HOST_INFO'),
'baseUrl' => env('API_BASE_URL'),
],
require(Yii::getAlias('@api/config/_urlManager.php'))
),

9. Edit \console\controllers\AppController.php. A search in this file reveals 6 instances of backend; copy 3 of them accordingly as api, as shown in Figure 9
public $writablePaths = [
'@common/runtime',
'@frontend/runtime',
'@frontend/web/assets',
'@backend/runtime',
'@backend/web/assets',
'@api/runtime',
'@api/web/assets',
'@storage/cache',
'@storage/web/source'
];
public $executablePaths = [
'@backend/yii',
'@api/yii',
'@frontend/yii',
'@console/yii',
];

10. Edit \autocompletion.php to add configurations related to the API application, as shown in Figure 10
* @property yii\web\UrlManager $urlManagerApi UrlManager for api application.

11. In the api directory, search for backend\, backend/, => ‘backend’, and BACKEND with strict case matching, and replace them with api\, api/, => ‘api’, and API respectively, as shown in Figure 11
Replace backend\ with: api\
Replace backend/ with: api/
Replace => 'backend' with: => 'api'
Replace BACKEND with: => API

12. The \tests directory also contains corresponding configurations related to the new API application. Hold off on this part for now and configure it when automated testing is needed. View \api\web\index-test.php, as shown in Figure 12

13. In Windows PowerShell, navigate to the E:\wwwroot\cmcp-api directory and run the command again: php console/yii app/setup to set writable and executable permissions, as shown in Figure 13

14. Add a new virtual host in the Nginx configuration file and restart Nginx, as shown in Figure 14
## API ##
server {
listen 80; ## 监听 ipv4 上的 80 端口
# listen [::]:80 default_server ipv6only=on; ## 监听 ipv6 上的 80 端口
root E:/wwwroot/cmcp-api/api/web;
index index.php index.html;
server_name www.cmcp-api.localhost;
charset utf-8;
access_log logs/www.cmcp-api.localhost.access.log;
error_log logs/www.cmcp-api.localhost.error.log;
client_max_body_size 128M;
# There is a VirtualBox bug related to sendfile that can lead to
# corrupted files, if not turned-off on Vagrant based setup
# sendfile off;
location / {
# 如果找不到真实存在的文件,把请求分发至 index.php
try_files $uri $uri/ /index.php?$args;
}
# location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|pdf|ppt|txt|bmp|rtf|js)$ {
# access_log off;
# expires max;
# }
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass php-fpm;
fastcgi_index index.php;
include fastcgi_params;
}
location ~ /\.(ht|svn|git) {
deny all;
}
}

15. Add configurations to the hosts file, as shown in Figure 15
# Content management platform API
127.0.0.1 cmcp-api.localhost www.cmcp-api.localhost frontend.cmcp-api.localhost backend.cmcp-api.localhost storage.cmcp-api.localhost

16. Open http://www.cmcp-api.localhost. If the style files fail to load and return a 404 response, you can delete all subdirectories under \api\web\assets, as shown in Figure 16

17. Prepare to implement the page creation functionality based on the API, as shown in Figure 17

18. Adjust the relevant structure of the Page model to implement model layering. Reference URL: https://www.shuijingwanwq.com/2017/08/15/1713/ , as shown in Figure 18

19. Open the URL: http://frontend.cmcp-api.localhost/gii , as shown in Figure 19

20. Generate the corresponding model based on the database table ca_page, as shown in Figure 20

21. Create a logics directory in the common directory to serve as the location for the logic layer of MySQL models, as shown in Figure 21

22. Copy \common\models\Page.php to \common\logics\Page.php, as shown in Figure 22
<?php namespace common\models; use Yii; use yii\behaviors\SluggableBehavior; use yii\behaviors\TimestampBehavior; use yii\db\ActiveRecord; /** * This is the model class for table "page". * * @property integer $id * @property string $slug * @property string $title * @property string $body * @property string $view * @property integer $status * @property integer $created_at * @property integer $updated_at */ class Page extends ActiveRecord { const STATUS_DRAFT = 0; const STATUS_PUBLISHED = 1; /** * @inheritdoc */ public static function tableName() { return '{{%page}}'; } /** * @inheritdoc */ public function behaviors() { return [ TimestampBehavior::className(), 'slug' => [
'class' => SluggableBehavior::className(),
'attribute' => 'title',
'ensureUnique' => true,
'immutable' => true
]
];
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['title', 'body'], 'required'],
[['body'], 'string'],
[['status'], 'integer'],
[['slug'], 'unique'],
[['slug'], 'string', 'max' => 2048],
[['title'], 'string', 'max' => 512],
[['view'], 'string', 'max' => 255]
];
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'id' => Yii::t('common', 'ID'),
'slug' => Yii::t('common', 'Slug'),
'title' => Yii::t('common', 'Title'),
'body' => Yii::t('common', 'Body'),
'view' => Yii::t('common', 'Page View'),
'status' => Yii::t('common', 'Active'),
'created_at' => Yii::t('common', 'Created At'),
'updated_at' => Yii::t('common', 'Updated At'),
];
}
}
23. Edit \common\logics\Page.php based on diff, as shown in Figure 23

24. The MySQL model file in the common/logics directory handles business logic and inherits from the \common\models\Page data layer, as shown in Figure 24
<?php namespace common\logics; use Yii; use yii\behaviors\SluggableBehavior; use yii\behaviors\TimestampBehavior; class Page extends \common\models\Page { const STATUS_DRAFT = 0; const STATUS_PUBLISHED = 1; /** * @inheritdoc */ public function behaviors() { return [ TimestampBehavior::className(), 'slug' => [
'class' => SluggableBehavior::className(),
'attribute' => 'title',
'ensureUnique' => true,
'immutable' => true
]
];
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['title', 'body'], 'required'],
[['body'], 'string'],
[['status'], 'integer'],
[['slug'], 'unique'],
[['slug'], 'string', 'max' => 2048],
[['title'], 'string', 'max' => 512],
[['view'], 'string', 'max' => 255],
];
}
}
25. Click the Start button under Model Generator to generate the Page model with the namespace common\models. At this point, internationalization support is required, so overwrite \common\models\Page.php, as shown in Figure 25

26. Edit \common\config\base.php. When the source language and target language are the same, set whether to force message translation. The default is false; set it to true, as shown in Figure 26
'*'=> [
'class' => 'yii\i18n\PhpMessageSource',
'forceTranslation' => true,
'basePath'=>'@common/messages',
'fileMap'=>[
'common'=>'common.php',
'backend'=>'backend.php',
'api'=>'api.php',
'frontend'=>'frontend.php',
],
'on missingTranslation' => ['\backend\modules\i18n\Module', 'missingTranslation']
],

27. Create \common\messages\en\model\page.php to support message translation when the target language is English (United States), as shown in Figure 27
return [
'ID' => 'ID',
'Slug' => 'Slug',
'Title' => 'Title',
'Body' => 'Body',
'View' => 'Page View',
'Status' => 'Active',
'Created At' => 'Created At',
'Updated At' => 'Updated At',
];

28. Create \common\messages\zh\model\page.php to support message translation when the target language is Simplified Chinese, as shown in Figure 28
return [
'ID' => 'ID',
'Slug' => '别名',
'Title' => '标题',
'Body' => '内容',
'View' => '页面浏览',
'Status' => '活动',
'Created At' => '创建时间',
'Updated At' => '更新时间',
];

29. Create \api\models\Page.php. The MySQL model file in the api/models directory handles business logic (specific to the api application only) and inherits from the \common\logics\Page logic layer, as shown in Figure 29

30. Copy \api\models\Page.php to \frontend\models\Page.php and \backend\models\Page.php, adjusting them to their respective namespaces, as shown in Figure 30

31. In the api application, search for use common\models\Page; and replace it with: use api\models\Page;. Apply similar processing in the frontend and backend applications, as shown in Figure 31

32. When the locale is set to Simplified Chinese, open http://backend.cmcp-api.localhost/page/create , and the translation feature works, as shown in Figure 32

33. Edit your profile to set the locale to English (United States), as shown in Figure 33

34. When the locale is set to English (United States), open http://backend.cmcp-api.localhost/page/create again, and the translation feature works, as shown in Figure 34

35. Start implementing the RESTful Web service. Reference URL: https://www.shuijingwanwq.com/2016/11/28/1457/ , as shown in Figure 35

36. Open the URL: http://backend.cmcp-api.localhost/article/create to create an article, as shown in Figure 36

37. In Postman, send a GET request to http://frontend.cmcp-api.localhost/api/v1/articles . This uses the built-in RESTful Web service, and the response result is shown in Figure 37
{
"items": [
{
"id": 1,
"slug": "biao-ti20180104-0",
"category_id": 1,
"title": "标题20180104-0",
"body": "
内容
",
"published_at": 1515031309,
"_links": {
"self": {
"href": "http://frontend.cmcp-api.localhost/api/v1/articles/1"
}
}
}
],
"_links": {
"self": {
"href": "http://frontend.cmcp-api.localhost/api/v1/articles?page=1"
}
},
"_meta": {
"totalCount": 1,
"pageCount": 1,
"currentPage": 1,
"perPage": 20
}
}

38. For RESTful Web services, it is recommended to implement them based on a standalone API application rather than an api module within the frontend application. This makes it more convenient to maintain your web application, as shown in Figure 38

39. Create a new directory: \api\rests. This directory will serve as the directory for action method classes of the RESTful Web service, as shown in Figure 39

40. Edit the controller class \api\controllers\PageController.php . The controller class extends from [[yii\rest\ActiveController]]. By specifying [[yii\rest\ActiveController::modelClass|modelClass]] as api\models\Page, the controller knows which model to use for fetching and processing data. As shown in Figure 40
namespace api\controllers;
use yii\rest\ActiveController;
class PageController extends ActiveController
{
public $modelClass = 'api\models\Page';
}
![Edit the controller class \api\controllers\PageController.php . The controller class extends from [[yii\rest\ActiveController]]. By specifying [[yii\rest\ActiveController::modelClass|modelClass]] as api\models\Page, the controller knows which model to use for fetching and processing data.](https://media.shuijingwanwq.com/2018/01/40.png)
41. Configure URL rules by modifying the configuration of the urlManager component in the application configuration. Edit: \api\config\_urlManager.php, as shown in Figure 41
return [
'class' => yii\web\UrlManager::class,
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => ['page'],
],
],
];

42. In Postman, send a GET request to http://www.cmcp-api.localhost/pages , which returns a 403 response, as shown in Figure 42
{
"name": "Forbidden",
"message": "Login Required",
"code": 0,
"status": 403,
"type": "yii\\web\\ForbiddenHttpException"
}

43. Edit \api\config\web.php and comment out the attached behavior globalAccess, to be adjusted later (authentication, authorization), as shown in Figure 43
/*
'as globalAccess' => [
'class' => common\behaviors\GlobalAccessBehavior::class,
'rules' => [
[
'controllers' => ['sign-in'],
'allow' => true,
'roles' => ['?'],
'actions' => ['login']
],
[
'controllers' => ['sign-in'],
'allow' => true,
'roles' => ['@'],
'actions' => ['logout']
],
[
'controllers' => ['site'],
'allow' => true,
'roles' => ['?', '@'],
'actions' => ['error']
],
[
'controllers' => ['debug/default'],
'allow' => true,
'roles' => ['?'],
],
[
'controllers' => ['user'],
'allow' => true,
'roles' => ['administrator'],
],
[
'controllers' => ['user'],
'allow' => false,
],
[
'allow' => true,
'roles' => ['manager'],
]
]
]
*/

44. In Postman, send a GET request to http://www.cmcp-api.localhost/pages , which returns a 200 response, as shown in Figure 44
[
{
"id": 1,
"slug": "about",
"title": "About",
"body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"view": null,
"status": 1,
"created_at": 1514860785,
"updated_at": 1514860785
}
]

45. The data returned after calling the API service uses a unified format: an HTTP status code of 20x indicates a successful call, while an HTTP status code of 4xx or 5xx indicates a failed call. Adjust the return data format of the 200 response to be consistent with the 403 response, containing at least: “message”, “code”. Create language pack files: \api\messages\zh\app.php (successful response), \api\messages\zh\error.php (failed response), as shown in Figure 45
return [
10000 => 'success',
10001 => '获取页面列表成功',
];

46. For versioning implementation, reference URL: https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/rest-versioning.md . Edit \api\controllers\PageController.php and remove modelClass, as shown in Figure 46
<?php
namespace api\controllers;
use yii\rest\ActiveController;
class PageController extends ActiveController
{
}
47. Implement each major version of the API in a separate module ID representing the major version number. Generate module v1 based on Gii by opening the URL: http://frontend.cmcp-api.localhost/gii/module , as shown in Figure 47

48. Create \api\modules\v1\models\Page.php, which inherits from \api\models\Page.php, as shown in Figure 48
Note: \api\modules\v1\models\Page (used only for the v1 module) > \api\models\Page (used only for the api application) > \common\logics\Page.php (can be used across multiple applications like api, frontend, etc.) > \common\models\Page.php (limited to Gii generation) > \yii\db\ActiveRecord
<?php
namespace api\modules\v1\models;
class Page extends \api\models\Page
{
}
49. Rename \api\modules\v1\controllers\DefaultController.php to \api\modules\v1\controllers\PageController.php and edit the code, as shown in Figure 49
Note: \api\modules\v1\controllers\PageController.php (used only for the v1 module) > \api\controllers\PageController.php (used only for the api application) > \yii\rest\ActiveController
<?php
namespace api\modules\v1\controllers;
/**
* Page controller for the `v1` module
*/
class PageController extends \api\controllers\PageController
{
public $modelClass = 'api\modules\v1\models\Page';
}
50. 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\web.php, as shown in Figure 50
'modules' => [
'v1' => [
'class' => api\modules\v1\Module::class,
],
'i18n' => [
'class' => api\modules\i18n\Module::class,
'defaultRoute' => 'i18n-message/index'
]
],
![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\web.php](https://media.shuijingwanwq.com/2018/01/50.png)
51. Configure URL rules by modifying the configuration of the urlManager component in the application configuration to support the v1 module. Edit: \api\config\_urlManager.php, as shown in Figure 51
Note: By configuring the only option to explicitly list which actions are supported, v1/page only supports: ‘index’, ‘update’, ‘delete’, ‘options’
<?php return [ 'class' => yii\web\UrlManager::class,
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => ['v1/page'],
'only' => ['index', 'update', 'delete', 'options'],
],
],
];
52. In Postman, send a GET request to http://www.cmcp-api.localhost/v1/pages , which returns a 200 response, as shown in Figure 52
[
{
"id": 1,
"slug": "about",
"title": "About",
"body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"view": null,
"status": 1,
"created_at": 1514860785,
"updated_at": 1514860785
}
]

53. Test the only option. In Postman, send a GET request to http://www.cmcp-api.localhost/v1/pages/1 , which returns a 405 response (Method Not Allowed. Because the ‘view’ action is not supported). The test passes, as shown in Figure 53

54. Configure URL rules by modifying the configuration of the urlManager component in the application configuration to support the v1 module. Edit: \api\config\_urlManager.php, as shown in Figure 54
Note: Uncomment the only option configuration to subsequently support all actions
<?php return [ 'class' => yii\web\UrlManager::class,
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => ['v1/page'],
],
],
];
55. In Postman, send a GET request to http://www.cmcp-api.localhost/v1 , which returns a 404 response in HTML format, as shown in Figure 55
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>
<meta name="csrf-param" content="_csrf">
<meta name="csrf-token" content="MtUm8pVxEHfmqhYMruxdUyScsktaMyFDc350jW_XR7dzmGqaoDRyT7bPc13M2W0AVNuGISJSbQUYOFn9LKNzgQ==">
<title>Not Found (#404)</title>
<link href="/assets/95b60493/themes/smoothness/jquery-ui.css?v=1514958071" rel="stylesheet">
<link href="/assets/7635f0fe/css/bootstrap.css?v=1512021376" rel="stylesheet">
<link href="/assets/1ee3df8/css/font-awesome.min.css?v=1512021376" rel="stylesheet">
<link href="/assets/541f775b/css/AdminLTE.min.css?v=1512021373" rel="stylesheet">
<link href="/assets/541f775b/css/skins/_all-skins.min.css?v=1512021373" rel="stylesheet">
<link href="/css/style.css?v=1512021342" rel="stylesheet">
</head>
<body class=" skin-blue ">
</body>
</html>
56. To resolve the 404 response being in HTML format, edit \api\config\web.php to set the default response format to JSON, as shown in Figure 56
'response' => [
'format' => yii\web\Response::FORMAT_JSON,
],

57. In Postman, send a GET request to http://www.cmcp-api.localhost/v1 , which returns a 404 response in JSON format, as shown in Figure 57
{
"name": "Not Found",
"message": "Page not found.",
"code": 0,
"status": 404,
"type": "yii\\web\\NotFoundHttpException"
}

58. Implement data serialization by including pagination information in the response body to simplify client development. Edit \api\controllers\PageController.php, as shown in Figure 58
<?php namespace api\controllers; use yii\rest\ActiveController; class PageController extends ActiveController { public $serializer = [ 'class' => 'yii\rest\Serializer',
'collectionEnvelope' => 'items',
];
}
59. In Postman, send a GET request to http://www.cmcp-api.localhost/v1/pages , which returns a 200 response with pagination information included in the response body, as shown in Figure 59
{
"items": [
{
"id": 1,
"slug": "about",
"title": "About",
"body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"view": null,
"status": 1,
"created_at": 1514860785,
"updated_at": 1514860785
}
],
"_links": {
"self": {
"href": "http://www.cmcp-api.localhost/v1/pages?page=1"
}
},
"_meta": {
"totalCount": 1,
"pageCount": 1,
"currentPage": 1,
"perPage": 20
}
}

60. RESTful APIs are typically stateless; therefore, configure the user application component. Edit \api\config\web.php, as shown in Figure 60
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' => [
'class' => yii\web\User::class,
'identityClass' => common\models\User::class,
'enableSession' => false,
'loginUrl' => null,
'enableAutoLogin' => false,
'as afterLogin' => common\behaviors\LoginTimestampBehavior::class
],

61. Copy Action.php, IndexAction.php, ViewAction.php, CreateAction.php, UpdateAction.php, DeleteAction.php, and Serializer.php from the \vendor\yiisoft\yii2\rest directory to the \api\rests\page directory, as shown in Figure 61

62. 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 . Edit \api\controllers\PageController.php, as shown in Figure 62
<?php namespace api\controllers; use yii\rest\ActiveController; class PageController extends ActiveController { public $serializer = [ 'class' => 'api\rests\page\Serializer',
'collectionEnvelope' => 'items',
];
/**
* @inheritdoc
*/
public function actions()
{
return [
'index' => [
'class' => 'api\rests\page\IndexAction',
'modelClass' => $this->modelClass,
'checkAccess' => [$this, 'checkAccess'],
],
'view' => [
'class' => 'api\rests\page\ViewAction',
'modelClass' => $this->modelClass,
'checkAccess' => [$this, 'checkAccess'],
],
'create' => [
'class' => 'api\rests\page\CreateAction',
'modelClass' => $this->modelClass,
'checkAccess' => [$this, 'checkAccess'],
'scenario' => $this->createScenario,
],
'update' => [
'class' => 'api\rests\page\UpdateAction',
'modelClass' => $this->modelClass,
'checkAccess' => [$this, 'checkAccess'],
'scenario' => $this->updateScenario,
],
'delete' => [
'class' => 'api\rests\page\DeleteAction',
'modelClass' => $this->modelClass,
'checkAccess' => [$this, 'checkAccess'],
],
'options' => [
'class' => 'yii\rest\OptionsAction',
],
];
}
}Note: If only a few actions need to be supported, you can choose the following approach, for example:
public function actions()
{
$actions = parent::actions();
$actions['view']['class'] = 'api\rests\page\ViewAction';
return $actions;
}

63. Edit \api\rests\page\IndexAction.php to adjust the namespace, inheritance relationship, query conditions, etc., as shown in Figure 63
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace api\rests\page;
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_INACTIVE = 0; //状态:不活跃
const STATUS_ACTIVE = 1; //状态:活跃
/**
* 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,
],
]);
}
}

64. Edit \api\rests\page\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., as shown in Figure 64
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace api\rests\page;
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('app', '10001'), 'data' => array_merge($result, $this->serializePagination($pagination))];
}
return ['code' => 10000, 'message' => Yii::t('app', '10001'), 'data' => $result];
}
}

65. Edit \api\config\base.php to configure the i18n application component for the API application, as shown in Figure 65
'i18n' => [
'translations' => [
'model/*'=> [
'class' => 'yii\i18n\PhpMessageSource',
'forceTranslation' => true,
'basePath'=>'@common/messages',
'fileMap'=>[
],
],
'app'=> [
'class' => 'yii\i18n\PhpMessageSource',
'forceTranslation' => true,
'basePath'=>'@api/messages',
'fileMap'=>[
],
],
'*'=> [
'class' => 'yii\i18n\PhpMessageSource',
'forceTranslation' => true,
'basePath'=>'@api/messages',
'fileMap'=>[
],
],
],
],

66. Create language pack file: \api\messages\zh\error.php (Simplified Chinese, failed response), as shown in Figure 66
return [
20000 => 'error',
20001 => '页面列表为空',
];

67. Create language pack file: \api\messages\en\app.php (English (United States), successful response), as shown in Figure 67
return [
10000 => 'success',
10001 => 'Get page list is successful',
];

68. Create language pack file: \api\messages\en\error.php (English (United States), failed response), as shown in Figure 68
return [
20000 => 'error',
20001 => 'Page list is empty',
];

69. Open the URL: http://backend.cmcp-api.localhost/page/update?id=1 to edit the article and uncheck the active checkbox, as shown in Figure 69

70. In Postman, send a GET request to http://www.cmcp-api.localhost/v1/pages , which returns a 200 response. The list of active pages is empty, as shown in Figure 70
Note:
Accept application/json; version=0.0
{
"code": 20001,
"message": "Page list is empty"
}

71. In Postman, send a GET request to http://www.cmcp-api.localhost/v1/pages , which returns a 200 response. The list of active pages is empty, as shown in Figure 71
Note:
Accept application/json; version=0.0
Accept-Language zh-CN
{
"code": 20001,
"message": "页面列表为空"
}

72. Open the URL: http://backend.cmcp-api.localhost/page/update?id=1 to edit the article and check the active checkbox, as shown in Figure 72

73. In Postman, send a GET request to http://www.cmcp-api.localhost/v1/pages , which returns a 200 response. The list of active pages is not empty, as shown in Figure 73
Note:
Accept application/json; version=0.0
Accept-Language en-US
{
"code": 10000,
"message": "Get page list is successful",
"data": {
"items": [
{
"id": 1,
"slug": "about",
"title": "About",
"body": "
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
",
"view": "",
"status": 1,
"created_at": 1514860785,
"updated_at": 1515482758
}
],
"_links": {
"self": {
"href": "http://www.cmcp-api.localhost/v1/pages?page=1"
}
},
"_meta": {
"totalCount": 1,
"pageCount": 1,
"currentPage": 1,
"perPage": 20
}
}
}

74. In Postman, send a GET request to http://www.cmcp-api.localhost/v1/pages , which returns a 200 response. The list of active pages is not empty, as shown in Figure 74
Note:
Accept application/json; version=0.0
Accept-Language zh-CN
{
"code": 10000,
"message": "获取页面列表成功",
"data": {
"items": [
{
"id": 1,
"slug": "about",
"title": "About",
"body": "
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
",
"view": "",
"status": 1,
"created_at": 1514860785,
"updated_at": 1515482758
}
],
"_links": {
"self": {
"href": "http://www.cmcp-api.localhost/v1/pages?page=1"
}
},
"_meta": {
"totalCount": 1,
"pageCount": 1,
"currentPage": 1,
"perPage": 20
}
}
}

75. GET /pages/1: Returns the detailed information of page 1. Edit \api\rests\page\Action.php to adjust the namespace, inheritance relationship, response structure, etc., as shown in Figure 75
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace api\rests\page;
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);
}
}

76. Edit \api\rests\page\ViewAction.php to adjust the namespace, inheritance relationship, response structure, etc., as shown in Figure 76
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace api\rests\page;
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_INACTIVE = 0; //状态:不活跃
const STATUS_ACTIVE = 1; //状态:活跃
/**
* 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_INACTIVE) {
return ['code' => 20003, 'message' => Yii::t('error', Yii::t('error', Yii::t('error', '20003'), ['id' => $id]))];
}
return ['code' => 10000, 'message' => Yii::t('app', '10002'), 'data' => $model];
}
}

77. Edit the language pack files: \api\messages\zh\app.php (Simplified Chinese, successful response), \api\messages\zh\error.php (Simplified Chinese, failed response), \api\messages\en\app.php (English (United States), successful response), \api\messages\en\error.php (English (United States), failed response), as shown in Figure 77
\api\messages\zh\app.php
return [
10000 => 'success',
10001 => '获取页面列表成功',
10002 => '获取页面详情成功',
];
\api\messages\zh\error.php
return [
20000 => 'error',
20001 => '页面列表为空',
20002 => '页面ID:{id},不存在',
20003 => '页面ID:{id},的状态不活跃',
];
\api\messages\en\app.php
return [
10000 => 'success',
10001 => 'Get page list is successful',
10002 => 'Get page details succeeded',
];
\api\messages\en\error.php
return [
20000 => 'error',
20001 => 'Page list is empty',
20002 => 'Page ID: {id}, does not exist',
20003 => 'Page ID: {id}, the status is not active',
];

78. In Postman, send a GET request to http://www.cmcp-api.localhost/v1/pages/1 , which returns a 200 response. Its status is active, as shown in Figure 78
Note:
Accept application/json; version=0.0
Accept-Language zh-CN
{
"code": 10000,
"message": "获取页面详情成功",
"data": {
"id": 2,
"slug": "contact",
"title": "Contact",
"body": "
Contact
",
"view": "",
"status": 1,
"created_at": 1515488912,
"updated_at": 1515488912
}
}

79. In Postman, send a GET request to http://www.cmcp-api.localhost/v1/pages/1 , which returns a 200 response. Its status is active, as shown in Figure 79
Note:
Accept application/json; version=0.0
Accept-Language en-US
{
"code": 10000,
"message": "Get page details succeeded",
"data": {
"id": 2,
"slug": "contact",
"title": "Contact",
"body": "
Contact
",
"view": "",
"status": 1,
"created_at": 1515488912,
"updated_at": 1515488912
}
}

80. In Postman, send a GET request to http://www.cmcp-api.localhost/v1/pages/2 , which returns a 200 response. Its status is inactive, as shown in Figure 80
Note:
Accept application/json; version=0.0
Accept-Language en-US
{
"code": 20003,
"message": "Page ID: 2, the status is not active"
}

81. In Postman, send a GET request to http://www.cmcp-api.localhost/v1/pages/3 , which returns a 404 response. The page does not exist, as shown in Figure 81
Note:
Accept application/json; version=0.0
Accept-Language zh-CN
{
"name": "Not Found",
"message": "页面ID:3,不存在",
"code": 20002,
"status": 404,
"type": "yii\\web\\NotFoundHttpException"
}

82. Implement the [[yii\web\Linkable]] interface to support HATEOAS, returning links related to the resource object. Edit the resource class \api\models\Page.php, as shown in Figure 82
<?php namespace api\models; use yii\helpers\Url; use yii\web\Linkable; use yii\web\Link; class Page extends \common\logics\Page implements Linkable { /** * Returns a list of links. * * @return array the links */ public function getLinks() { return [ Link::REL_SELF => Url::to(['page/view', 'id' => $this->id], true),
'index' => Url::to(['page/index'], true),
'view' => Url::to(['page/view', 'id' => $this->id], true),
'create' => Url::to(['page/index'], true),
'update' => Url::to(['page/view', 'id' => $this->id], true),
'delete' => Url::to(['page/view', 'id' => $this->id], true),
'options' => Url::to(['page/index'], true),
];
}
}![Implement the [[yii\web\Linkable]] interface to support HATEOAS, returning links related to the resource object. Edit the resource class \api\models\Page.php](https://media.shuijingwanwq.com/2018/01/82.png)
83. In Postman, send a GET request to http://www.cmcp-api.localhost/v1/pages/1 , which returns a 200 response and supports HATEOAS, as shown in Figure 83
Note:
Accept application/json; version=0.0
Accept-Language zh-CN
Accept-Encoding gzip, deflate, br
{
"code": 10000,
"message": "获取页面详情成功",
"data": {
"id": 1,
"slug": "about",
"title": "About",
"body": "
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
",
"view": "",
"status": 1,
"created_at": 1514860785,
"updated_at": 1515548927,
"_links": {
"self": {
"href": "http://www.cmcp-api.localhost/v1/pages/1"
},
"index": {
"href": "http://www.cmcp-api.localhost/v1/pages"
},
"view": {
"href": "http://www.cmcp-api.localhost/v1/pages/1"
},
"create": {
"href": "http://www.cmcp-api.localhost/v1/pages"
},
"update": {
"href": "http://www.cmcp-api.localhost/v1/pages/1"
},
"delete": {
"href": "http://www.cmcp-api.localhost/v1/pages/1"
},
"options": {
"href": "http://www.cmcp-api.localhost/v1/pages"
}
}
}
}

84. POST /pages: Create a new page. Edit \api\rests\page\CreateAction.php, as shown in Figure 84
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace api\rests\page;
use Yii;
use yii\base\Model;
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);
}
/* @var $model \yii\db\ActiveRecord */
$model = new $this->modelClass([
'scenario' => $this->scenario,
]);
$model->load(Yii::$app->getRequest()->getBodyParams(), '');
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;
}
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('app', '10003'), 'data' => $model];
}
}

85. In Postman, send a POST request to http://www.cmcp-api.localhost/v1/pages , which returns a 201 response, as shown in Figure 85
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": {
"slug": "slug-20180110-4",
"title": "title-20180110-4",
"body": "body-20180110-4",
"view": "view-20180110-4",
"status": "0",
"created_at": 1515566824,
"updated_at": 1515566824,
"id": 7,
"_links": {
"self": {
"href": "http://www.cmcp-api.localhost/v1/pages/7"
},
"index": {
"href": "http://www.cmcp-api.localhost/v1/pages"
},
"view": {
"href": "http://www.cmcp-api.localhost/v1/pages/7"
},
"create": {
"href": "http://www.cmcp-api.localhost/v1/pages"
},
"update": {
"href": "http://www.cmcp-api.localhost/v1/pages/7"
},
"delete": {
"href": "http://www.cmcp-api.localhost/v1/pages/7"
},
"options": {
"href": "http://www.cmcp-api.localhost/v1/pages"
}
}
}
}

86. In Postman, send a POST request to http://www.cmcp-api.localhost/v1/pages with the parameters kept as they are, which returns a 422 response, as shown in Figure 86
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": "数据验证失败:Slug的值\"slug-20180110-4\"已经被占用了。"
}

87. In Postman, send a POST request to http://www.cmcp-api.localhost/v1/pages , which returns a 422 response (Data validation failed (for example, responding to a POST request). Please check the detailed error messages in the response body.), as shown in Figure 87
Note:
Accept application/json; version=0.0
Accept-Language zh-CN
Accept-Encoding gzip, deflate, br
Content-Type application/x-www-form-urlencoded
Body
Missing title parameter
{
"code": 20004,
"message": "数据验证失败:Title不能为空。"
}

88. In Postman, send a POST request to http://www.cmcp-api.localhost/v1/pages , which returns a 422 response, as shown in Figure 88
Note:
Accept application/json; version=0.0
Accept-Language en-US
Accept-Encoding gzip, deflate, br
Content-Type application/x-www-form-urlencoded
Body
Missing title parameter
{
"code": 20004,
"message": "Data validation failed: Title cannot be blank."
}

89. PUT /pages/4: Update a page. Edit \api\rests\page\UpdateAction.php, as shown in Figure 89
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace api\rests\page;
use Yii;
use yii\base\Model;
use yii\db\ActiveRecord;
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);
}
$model->scenario = $this->scenario;
$model->load(Yii::$app->getRequest()->getBodyParams(), '');
if ($model->save() === false) {
if ($model->hasErrors()) {
$response = Yii::$app->getResponse();
$response->setStatusCode(422, 'Data Validation Failed.');
foreach ($model->getFirstErrors() as $message) {
$firstErrors = $message;
}
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('app', '10004'), 'data' => $model];
}
}

90. In Postman, send a PUT request to http://www.cmcp-api.localhost/v1/pages/4 , which returns a 200 response, as shown in Figure 90
{
"code": 10000,
"message": "更新页面成功",
"data": {
"id": 4,
"slug": "slug-20180110-44",
"title": "title-20180110-44",
"body": "body-20180110-44",
"view": "view-20180110-44",
"status": "1",
"created_at": 1515554512,
"updated_at": 1515569633,
"_links": {
"self": {
"href": "http://www.cmcp-api.localhost/v1/pages/4"
},
"index": {
"href": "http://www.cmcp-api.localhost/v1/pages"
},
"view": {
"href": "http://www.cmcp-api.localhost/v1/pages/4"
},
"create": {
"href": "http://www.cmcp-api.localhost/v1/pages"
},
"update": {
"href": "http://www.cmcp-api.localhost/v1/pages/4"
},
"delete": {
"href": "http://www.cmcp-api.localhost/v1/pages/4"
},
"options": {
"href": "http://www.cmcp-api.localhost/v1/pages"
}
}
}
}

91. In Postman, send a PUT request to http://www.cmcp-api.localhost/v1/pages/4 , which returns a 422 response, as shown in Figure 91
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 slug has already been taken by another page
{
"code": 20004,
"message": "Data validation failed: Slug \"slug-20180110-5\" has already been taken."
}

92. DELETE /pages/4: Delete page 4. Edit \api\rests\page\DeleteAction.php, as shown in Figure 92
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace api\rests\page;
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('app', '10005')];
}
}

93. In Postman, send a DELETE request to http://www.cmcp-api.localhost/v1/pages/7 , which returns a 200 response, as shown in Figure 93
Note:
Accept application/json; version=0.0
Accept-Language zh-CN
Accept-Encoding gzip, deflate, br
{
"code": 10000,
"message": "删除页面成功"
}

94. Show the final contents of the language pack files, as shown in Figure 94
\api\messages\zh\app.php
return [
10000 => 'success',
10001 => '获取页面列表成功',
10002 => '获取页面详情成功',
10003 => '创建页面成功',
10004 => '更新页面成功',
10005 => '删除页面成功',
];
\api\messages\zh\error.php
return [
20000 => 'error',
20001 => '页面列表为空',
20002 => '页面ID:{id},不存在',
20003 => '页面ID:{id},的状态不活跃',
20004 => '数据验证失败:{firstErrors}',
];
\api\messages\en\app.php
return [
10000 => 'success',
10001 => 'Get page list is successful',
10002 => 'Get page details succeeded',
10003 => 'Create a page success',
10004 => 'Update page success',
10005 => 'Delete page success',
];
\api\messages\en\error.php
return [
20000 => 'error',
20001 => 'Page list is empty',
20002 => 'Page ID: {id}, does not exist',
20003 => 'Page ID: {id}, the status is not active',
20004 => 'Data validation failed: {firstErrors}',
];

95. OPTIONS /pages: Shows the verbs supported by the endpoint /pages. In Postman, send an OPTIONS request to http://www.cmcp-api.localhost/v1/pages , which returns a 200 response, as shown in Figure 95

96. OPTIONS /pages/1: Shows the verbs supported by the endpoint /pages/1. In Postman, send an OPTIONS request to http://www.cmcp-api.localhost/v1/pages/1 , which returns a 200 response, as shown in Figure 96

97. Summary: The currently supported actions are: index, view, create, update, delete, options. Except for options, they are basically implemented by overriding after inheritance to meet specific requirements.
