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,皆是继承之后,再次覆写实现具体的需求。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/250407.html
![新建控制器类 /api/controllers/UserController.php ,控制器类扩展自 [[yii/rest/ActiveController]]。数据序列化的实现,在响应主体内包含分页信息来简化客户端的开发工作](https://blog.ytso.com/wp-content/themes/justnews/themer/assets/images/lazy.png)