第六章 RESTful web服务

在本章中,我们将会讨论如下话题:

  • 创建一个REST服务器
  • 身份校验
  • 频率限制
  • 版本控制
  • 错误处理

介绍

本章将会帮助你了解一些便利的东西,包括Yii URL路由,控制器和视图。你将能够更加灵活的创建自己的控制器和视图。

创建一个REST服务器

在接下来的章节中,我们使用了一个例子,给你演示如何用最少的代码,简历和设置RESTful API。这小节会在本章的其它小节中复用。

准备

  1. 按照官方指南http://www.yiiframework.com/doc-2.0/guide-start-installation.html的描述,使用Composer包管理器创建一个新的应用。
  2. 使用如下命令创建一个migration,用于创建一个文章表:
  1. ./yii migrate/create create_film_table
  1. 然后,使用如下代码更新刚刚创建的migration方法,up
  1. public function up()
  2. {
  3. $tableOptions = null;
  4. if ($this->db->driverName === 'mysql') {
  5. $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB';
  6. }
  7. $this->createTable('{{%film}}', [
  8. 'id' => $this->primaryKey(),
  9. 'title' => $this->string(64)->notNull(),
  10. 'release_year' => $this->integer(4)->notNull(),
  11. ], $tableOptions);
  12. $this->batchInsert('{{%film}}',
  13. ['id','title','release_year'], [
  14. [1, 'Interstellar', 2014],
  15. [2, "Harry Potter and the Philosopher's Stone",2001],
  16. [3, 'Back to the Future', 1985],
  17. [4, 'Blade Runner', 1982],
  18. [5, 'Dallas Buyers Club', 2013],
  19. ]);
  20. }

使用如下代码更新down方法:

  1. public function down()
  2. {
  3. $this->dropTable('film');
  4. }
  1. 运行创建好的migration create_film_table
  2. 使用Gii生成Film模型。
  3. 使用整洁的URL配置你的应用服务器。如果你使用带有mod_rewrite的Apache服务器,并将AllowOverride打开,那么你就可以添加如下代码到@web目录下的.htaccess文件中:
  1. Options +FollowSymLinks
  2. IndexIgnore */*
  3. RewriteEngine on
  4. # if a directory or a file exists, use it directly
  5. RewriteCond %{REQUEST_FILENAME} !-f
  6. RewriteCond %{REQUEST_FILENAME} !-d
  7. # otherwise forward it to index.php
  8. RewriteRule . index.php

如何做…

  1. 使用如下代码创建控制器@app/controller/FilmController.php
  1. <?php
  2. namespace app\controllers;
  3. use yii\rest\ActiveController;
  4. class FilmController extends ActiveController
  5. {
  6. public $modelClass = app\models\Film';
  7. }

更新配置文件@app/config/web.php。添加urlManager组件:

  1. 'urlManager' => [
  2. 'enablePrettyUrl' => true,
  3. 'enableStrictParsing' => true,
  4. 'showScriptName' => false,
  5. 'rules' => [
  6. ['class' => 'yii\rest\UrlRule', 'controller' =>
  7. 'films'],
  8. ],
  9. ],
  1. @app/config/web.php中重新配置请求组件:
  1. 'request' => [
  2. 'cookieValidationKey' => 'mySecretKey',
  3. 'parsers' => [
  4. 'application/json' => 'yii\web\JsonParser',
  5. ],
  6. ]

工作原理…

我们通过扩展\yii\rest\ActiveController来创建自己的控制器,然后设置这个控制器的modelClass属性。\yii\rest\ActiveController实现了常用动作的集合,以支持对ActiveRecord的RESTful访问。

上边通过很小的努力,就实现了对电影数据的RESTful API访问。

已经创建的API包括:

  • GET /films:按页列出所有的电影
  • HEAD /films:获取电影列表的概要信息
  • POST /films:创建一个新的电影
  • GET /films/5:获取电影5的详情
  • HEAD /films/5:获取电影5的概要信息
  • PATCH /films/5 and PUT /films/5:更新电影5
  • DELETE /films/5:删除电影5
  • OPTIONS /films:显示/films支持的操作
  • OPTIONS /films/5:显示/films/5支持的操作

之所以会有这些功能是因为\yii\rest\ActiveController支持如下动作:

  • index:列出模型
  • view:返回模型的细节
  • create:创建一个新的模型
  • update:更新一个已有的模型
  • delete:删除一个已有的模型
  • options:返回允许的HTTP方法

verbs()方法为每一个动作定义了允许的请求方法。

为了检查我们的RESTful API是否工作正常,我们来发送几个请求。

首先使用GET请求。在控制台中运行如下命令:

  1. curl -i -H "Accept:application/json" "http://yii-book.app/films"

你将会得到如下输出:

  1. HTTP/1.1 200 OK
  2. Date: Wed, 23 Sep 2015 17:46:35 GMT
  3. Server: Apache
  4. X-Powered-By: PHP/5.5.23
  5. X-Pagination-Total-Count: 5
  6. X-Pagination-Page-Count: 1
  7. X-Pagination-Current-Page: 1
  8. X-Pagination-Per-Page: 20
  9. Link: <http://yii-book.app/films?page=1>; rel=self
  10. Content-Length: 301
  11. Content-Type: application/json; charset=UTF-8
  12. [{"id":1,"title":"Interstellar","release_year":2014},{"id":2,"title":
  13. "Harry Potter and the Philosopher's
  14. Stone","release_year":2001},{"id":3,"title":"Back to the
  15. Future","release_year":1985},{"id":4,"title":"Blade
  16. Runner","release_year":1982},{"id":5,"title":"Dallas Buyers
  17. Club","release_year":2013}]

发送一个POST请求。在控制台中运行如下命令:

  1. curl -i -H "Accept:application/json" -X POST -d title="New film" -d release_year=2015 "http://yii-book.app/films"

你将会得到如下输出:

  1. HTTP/1.1 201 Created
  2. Date: Wed, 23 Sep 2015 17:48:06 GMT
  3. Server: Apache
  4. X-Powered-By: PHP/5.5.23
  5. Location: http://yii-book.app/films/6
  6. Content-Length: 49
  7. Content-Type: application/json; charset=UTF-8
  8. {"title":"New film","release_year":"2015","id":6}

获取创建好的电影。在控制台中运行如下命令:

  1. curl -i -H "Accept:application/json" "http://yii-book.app/films/6"

你将会得到如下输出:

  1. HTTP/1.1 200 OK
  2. Date: Wed, 23 Sep 2015 17:48:36 GMT
  3. Server: Apache
  4. X-Powered-By: PHP/5.5.23
  5. Content-Length: 47
  6. Content-Type: application/json; charset=UTF-8
  7. {"id":6,"title":"New film","release_year":2015}

发送一个DELETE请求。在控制台中运行如下命令:

  1. curl -i -H "Accept:application/json" -X DELETE "http://yii-book.app/films/6"

你将会得到如下输出:

  1. HTTP/1.1 204 No Content
  2. Date: Wed, 23 Sep 2015 17:48:55 GMT
  3. Server: Apache
  4. X-Powered-By: PHP/5.5.23
  5. Content-Length: 0
  6. Content-Type: application/json; charset=UTF-8

更多…

接下来我们看下内容negotiation和自定义Rest URL规则:

Content negotiation

你也可以很容易使用内容negotiation行为格式化你的响应:

例如,你可以将这个代码放入到你的控制器中,这样所有的数据都会以XML格式返回:

你应该看下文档中所有的格式列表。

  1. use yii\web\Response;
  2. public function behaviors()
  3. {
  4. $behaviors = parent::behaviors();
  5. $behaviors['contentNegotiator']['formats']['application/xml']=Response::FORMAT_XML;
  6. return $behaviors;
  7. }

在控制台中运行:

  1. curl -i -H "Accept:application/xml" "http://yii-book.app/films"

你将会得到如下输出:

  1. HTTP/1.1 200 OK
  2. Date: Wed, 23 Sep 2015 18:02:47 GMT
  3. Server: Apache
  4. X-Powered-By: PHP/5.5.23
  5. X-Pagination-Total-Count: 5
  6. X-Pagination-Page-Count: 1
  7. X-Pagination-Current-Page: 1
  8. X-Pagination-Per-Page: 20
  9. Link: <http://yii-book.app/films?page=1>; rel=self
  10. Content-Length: 516
  11. Content-Type: application/xml; charset=UTF-8
  12. <?xml version="1.0" encoding="UTF-8"?>
  13. <response>
  14. <item>
  15. <id>1</id>
  16. <title>Interstellar</title>
  17. <release_year>2014
  18. </release_year>
  19. </item>
  20. <item>
  21. <id>2</id>
  22. <title>Harry Potter and the Philosopher's Stone</title>
  23. <release_year>2001
  24. </release_year>
  25. </item>
  26. <item>
  27. <id>3</id>
  28. <title>Back to the Future</title>
  29. <release_year>1985
  30. </release_year>
  31. </item>
  32. <item>
  33. <id>4</id>
  34. <title>Blade Runner</title>
  35. <release_year>1982
  36. </release_year>
  37. </item>
  38. <item>
  39. <id>5</id>
  40. <title>Dallas Buyers Club</title>
  41. <release_year>2013
  42. </release_year>
  43. </item>
  44. </response>

自定义Rest URL规则

默认情况下,你必须记住一个以复杂格式定义的控制器ID。这是因为yii\rest\UrlRule自动复杂化控制器ID。你可以很容易的通过设置yii\rest\UrlRule::$pluralize的值为false,来关掉它:

  1. 'urlManager' => [
  2. //..
  3. 'rules' => [
  4. [
  5. 'class' => 'yii\rest\UrlRule',
  6. 'controller' => 'film'
  7. 'pluralize' => false
  8. ],
  9. ],
  10. //..
  11. ]

如果你希望控制器按照指定的格式出现,你可以添加一个自定义名称到键值对,其中键是控制器ID,值是真正的控制器ID,例如:

  1. 'urlManager' => [
  2. //..
  3. 'rules' => [
  4. [
  5. 'class' => 'yii\rest\UrlRule',
  6. 'controller' => ['super-films' => 'film']
  7. ],
  8. ],
  9. //..
  10. ]

参考

欲了解更多信息,参考如下地址:

身份校验

在本小节中我们将会设置身份校验模型。

准备

重复创建一个REST服务器小节中准备如何做的所有步骤。

如何做…

  1. 修改@app/controllers/FilmController
  1. <?php
  2. namespace app\controllers;
  3. use app\models\User;
  4. use Yii;
  5. use yii\helpers\ArrayHelper;
  6. use yii\rest\ActiveController;
  7. use yii\filters\auth\HttpBasicAuth;
  8. class FilmController extends ActiveController
  9. {
  10. public $modelClass = 'app\models\Film';
  11. public function behaviors()
  12. {
  13. return ArrayHelper::merge(parent::behaviors(),[
  14. 'authenticator' => [
  15. 'authMethods' => [
  16. 'basicAuth' => [
  17. 'class' =>HttpBasicAuth::className(),
  18. 'auth' => function ($username,$password) {
  19. $user =User::findByUsername($username);
  20. if ($user !== null && $user->validatePassword($password)){
  21. return $user;
  22. }
  23. return null;
  24. },
  25. ]
  26. ]
  27. ]
  28. ]);
  29. }
  30. }

在浏览器中打开http://yii-book.app/films,确保我们配置了HTTP基本身份验证:

第六章 RESTful web服务 - 图1

尝试身份验证。在控制台中运行如下命令:

  1. curl -i -H "Accept:application/json" "http://yii-book.app/films"

你将会得到如下结果:

  1. HTTP/1.1 401 Unauthorized
  2. Date: Thu, 24 Sep 2015 01:01:24 GMT
  3. Server: Apache
  4. X-Powered-By: PHP/5.5.23
  5. Www-Authenticate: Basic realm="api"
  6. Content-Length: 149
  7. Content-Type: application/json; charset=UTF-8
  8. {"name":"Unauthorized","message":"You are requesting with an invalid credential.","code":0,"status":401,"type":"yii\\web\\UnauthorizedHttp
  9. Exception"}
  1. 现在尝试使用cURL进行auth
  1. curl -i -H "Accept:application/json" -u admin:admin "http://yii-book.app/films"
  1. 你将会得到类似如下结果:
  1. HTTP/1.1 200 OK
  2. Date: Thu, 24 Sep 2015 01:01:40 GMT
  3. Server: Apache
  4. X-Powered-By: PHP/5.5.23
  5. Set-Cookie: PHPSESSID=8b3726040bf8850ebd07209090333103; path=/;
  6. HttpOnly
  7. Expires: Thu, 19 Nov 1981 08:52:00 GMT
  8. Cache-Control: no-store, no-cache, must-revalidate,
  9. post-check=0, pre-check=0
  10. Pragma: no-cache
  11. X-Pagination-Total-Count: 5
  12. X-Pagination-Page-Count: 1
  13. X-Pagination-Current-Page: 1
  14. X-Pagination-Per-Page: 20
  15. Link: <http://yii-book.app/films?page=1>; rel=self
  16. Content-Length: 301
  17. Content-Type: application/json; charset=UTF-8
  18. [{"id":1,"title":"Interstellar","release_year":2014},{"id":2,"ti
  19. tle":"Harry Potter and the Philosopher's
  20. Stone","release_year":2001},{"id":3,"title":"Back to the
  21. Future","release_year":1985},{"id":4,"title":"Blade
  22. Runner","release_year":1982},{"id":5,"title":"Dallas Buyers
  23. Club","release_year":2013}]

工作原理…

我们将authenticator行为添加到了HttpBasicAuth类中,所以我们将可以通过登录名和密码进行身份校验。你可以实现官方文档中RESTful web服务部分的任意身份校验方法。

更多…

其它发送access token的方法:

  • HTTP基础验证
  • 查询参数
  • OAuth

Yii支持所有这些身份校验方法。

参考

欲了解更多信息,参考http://www.yiiframework.com/doc-2.0/guide-rest-rate-limiting.html

频率限制

为了防止滥用,你应该考虑为API添加频率限制。例如,你希望每一个用户在一分钟的时间内,最多调用五次API。如果调用过多,将会返回状态码429(太多的请求)。

准备

重复创建一个REST服务器小节中准备如何做…的所有步骤。

  1. 创建一个migration,用于创建一个用户允许表,命令如下:
  1. ./yii migrate/create create_user_allowance_table
  1. 然后使用如下代码更新刚刚创建的migration,up
  1. public function up()
  2. {
  3. $tableOptions = null;
  4. if ($this->db->driverName === 'mysql') {
  5. $tableOptions = 'CHARACTER SET utf8 COLLATEutf8_general_ci ENGINE=InnoDB';
  6. }
  7. $this->createTable('{{%user_allowance}}', [
  8. 'user_id' => $this->primaryKey(),
  9. 'allowed_number_requests' => $this->integer(10)->notNull(),
  10. 'last_check_time' => $this->integer(10)->notNull()
  11. ], $tableOptions);
  12. }
  1. 使用如下代码更新down方法:
  1. public function down()
  2. {
  3. $this->dropTable('{{%user_allowance}}');
  4. }
  1. 运行创建的create_firm_table migration。
  2. 使用Gii模块生成UserAllowance模型。

如何做…

首先,你必须更新@app/controllers/FilmController.php

  1. <?php
  2. namespace app\controllers;
  3. use yii\rest\ActiveController;
  4. use yii\filters\RateLimiter;
  5. use yii\filters\auth\QueryParamAuth;
  6. class FilmController extends ActiveController
  7. {
  8. public $modelClass = 'app\models\Film';
  9. public function behaviors()
  10. {
  11. $behaviors = parent::behaviors();
  12. $behaviors['authenticator'] = [
  13. 'class' => QueryParamAuth::className(),
  14. ];
  15. $behaviors['rateLimiter'] = [
  16. 'class' => RateLimiter::className(),
  17. 'enableRateLimitHeaders' => true
  18. ];
  19. return $behaviors;
  20. }
  21. }

为了激活频率限制,User模型类应该实现yii\filters\RateLimitInterface,并实现三个方法:getRateLimit()loadAllowance()saveAllowance()。你需要添加RATE_LIMIT_NUMBERRATE_LIMIT_RESET两个常数:

  1. <?php
  2. namespace app\models;
  3. class User extends \yii\base\Object implements \yii\web\IdentityInterface, \yii\filters\RateLimitInterface
  4. {
  5. public $id;
  6. public $username;
  7. public $password;
  8. public $authKey;
  9. public $accessToken;
  10. const RATE_LIMIT_NUMBER = 5;
  11. const RATE_LIMIT_RESET = 60;
  12. // it means that user allowed only 5 requests per one minute
  13. public function getRateLimit($request, $action)
  14. {
  15. return [self::RATE_LIMIT_NUMBER,self::RATE_LIMIT_RESET];
  16. }
  17. public function loadAllowance($request, $action)
  18. {
  19. $userAllowance = UserAllowance::findOne($this->id);
  20. return $userAllowance ?
  21. [$userAllowance->allowed_number_requests,$userAllowance->last_check_time]
  22. :$this->getRateLimit($request, $action);
  23. }
  24. public function saveAllowance($request, $action,$allowance,
  25. $timestamp)
  26. {
  27. $userAllowance = ($allowanceModel =UserAllowance::findOne($this->id)) ?$allowanceModel : new UserAllowance();
  28. $userAllowance->user_id = $this->id;
  29. $userAllowance->last_check_time = $timestamp;
  30. $userAllowance->allowed_number_requests =$allowance;
  31. $userAllowance->save();
  32. }
  33. // other User model methods
  34. }

工作原理…

一旦验证类实现了需要的接口,Yii将会自动使用[[yii\rest\Controller]]动作过滤器中配置的[[\yii\filter\RateLimiter]]来运行频率检查。我们也需要为authenticator行为添加QueryParamAuth类。所以,我们现在能够只需要通过查询参数传递一个access token来进行身份校验。你可以添加官方文档中RESTful web服务部分中描述的任何一个身份验证方法。

下面来解释我们的方法,非常容易理解。

getRateLimit():返回允许请求的最大数量和时间段(例如,[100,600]表示600秒内最多运行100次API调用) loadAllowance():返回剩余运行请求的数量,以及上次检查频率限制的UNIX时间戳。 saveAllowance():保存剩余运行请求数量的值,以及当前UNIX时间戳。

我们将数据存放在MySQL数据库中。考虑到性能,你也许可以使用一个NoSQL数据库或者其它能更快获取和加载数据的存储系统。

现在我们来检查频率限制特性。运行如下代码:

  1. curl -i "http://yii-book.app/films?access-token=100-token"

你将会得到如下输出:

  1. HTTP/1.1 200 OK
  2. Date: Thu, 24 Sep 2015 01:35:51 GMT
  3. Server: Apache
  4. X-Powered-By: PHP/5.5.23
  5. Set-Cookie: PHPSESSID=495a928978cc732bee853b83f521eba2; path=/;
  6. HttpOnly
  7. Expires: Thu, 19 Nov 1981 08:52:00 GMT
  8. Cache-Control: no-store, no-cache, must-revalidate, post-check=0,
  9. pre-check=0
  10. Pragma: no-cache
  11. X-Rate-Limit-Limit: 5
  12. X-Rate-Limit-Remaining: 4
  13. X-Rate-Limit-Reset: 0
  14. X-Pagination-Total-Count: 5
  15. X-Pagination-Page-Count: 1
  16. X-Pagination-Current-Page: 1
  17. X-Pagination-Per-Page: 20
  18. Link: <http://yii-book.app/films?access-token=100-token&page=1>;
  19. rel=self
  20. Content-Length: 301
  21. Content-Type: application/json; charset=UTF-8
  22. [{"id":1,"title":"Interstellar","release_year":2014},{"id":2,"title":
  23. "Harry Potter and the Philosopher's
  24. Stone","release_year":2001},{"id":3,"title":"Back to the
  25. Future","release_year":1985},{"id":4,"title":"Blade
  26. Runner","release_year":1982},{"id":5,"title":"Dallas Buyers
  27. Club","release_year":2013}]

来看下返回的头部。当激活了频率限制,默认情况下每次响应将会发送当前频率限制的信息:

X-Rate-Limit-Limit:在一段时间内最大允许请求的数量 X-Rate-Limit-Remaining:在当前时间内容剩余的允许请求数量 X-Rate-Limit-Reset:这是需要等待的时间数,来获取最大数量的允许请求

现在尝试超过限制,在一分钟内运行超过5次,将会得到TooManyRequestsHttpExeption

  1. HTTP/1.1 429 Too Many Requests
  2. Date: Thu, 24 Sep 2015 01:37:24 GMT
  3. Server: Apache
  4. X-Powered-By: PHP/5.5.23
  5. Set-Cookie: PHPSESSID=bb630ca8a641ef92bd210c0a936e3149; path=/;
  6. HttpOnly
  7. Expires: Thu, 19 Nov 1981 08:52:00 GMT
  8. Cache-Control: no-store, no-cache, must-revalidate, post-check=0,
  9. pre-check=0
  10. Pragma: no-cache
  11. X-Rate-Limit-Limit: 5
  12. X-Rate-Limit-Remaining: 0
  13. X-Rate-Limit-Reset: 60
  14. Content-Length: 131
  15. Content-Type: application/json; charset=UTF-8
  16. {"name":"Too Many Requests","message":"Rate limit
  17. exceeded.","code":0,"status":429,"type":"yii\\web\\TooManyRequestsHtt
  18. pException"}

参考

欲了解更多信息,参考如下地址:

版本

如果你的API没有版本是非常可怕的。假设你有一个重大改变——和客户端开发者的计划相违背,例如重命名或者删除一个参数,或者改变响应的格式——你将冒着破坏你顾客系统的风险,进而导致愤怒的支持电话或者更糟糕的客户流失。这就是为什么你需要保持你的API是版本化的。在Yii2中,版本可以很容易的通过模块来完成,所以版本被认为是孤立的一块代码。

准备

重复创建一个REST服务器小节中准备如何做…的所有步骤。

如何做…

  1. 在你的app文件夹中创建如下结构。总的来说,你需要创建@app/modules文件夹以及v1v2子文件夹。在每一个模块文件中,你需要创建控制器和模型文件夹:
  1. app/
  2. modules/
  3. v1/
  4. controllers/
  5. FilmController.php
  6. Module.php
  7. v2/
  8. controllers/
  9. FilmController.php
  10. Module.php
  1. 添加导入模块到@app/config/web.php
  1. 'modules' => [
  2. 'v1' => [
  3. 'class' => 'app\modules\v1\Module',
  4. ],
  5. 'v2' => [
  6. 'class' => 'app\modules\v2\Module'
  7. ]
  8. ],
  1. 使用如下代码创建@app/modules/v1/controllers/FilmController.php@app/modules/v2/controllers/FilmController.php
  1. <?php
  2. namespace app\modules\v1\controllers;
  3. use yii\rest\ActiveController;
  4. class FilmController extends ActiveController
  5. {
  6. public $modelClass = 'app\models\Film';
  7. }
  1. <?php
  2. namespace app\modules\v1\controllers;
  3. use yii\rest\ActiveController;
  4. class FilmController extends ActiveController
  5. {
  6. public $modelClass = 'app\models\Film';
  7. }

使用如下代码创建@app/modules/v1/Module.php@app/modules/v2/Module.php

  1. <?php
  2. namespace app\modules\v1;
  3. class Module extends \yii\base\Module
  4. {
  5. public function init()
  6. {
  7. parent::init();
  8. }
  9. }
  1. <?php
  2. namespace app\modules\v2;
  3. class Module extends \yii\base\Module
  4. {
  5. public function init()
  6. {
  7. parent::init();
  8. }
  9. }

工作原理…

每一个模块代表我们API中的一个独立的版本:

现在你将能以两种方式指定API版本:

  1. 通过API URL。你可以指定v1或者v2版本。结果是http://yiibook.app/v1/film返回版本1的电影列表,http://yiibook.app/v2/film将会返回版本2的电影列表。
  2. 你可以通过HTTP请求头指定一个版本号。通常来说,这可以通过Accept头来完成:
  1. // as a vendor content type
  2. Accept: application/vnd.company.myproject-v1+json
  3. // via a parameter
  4. Accept: application/json; version=v1

所以,现在我们有了两个版本的API,我们可以很容易的修改v2版本。我们的老客户继续使用v1版本,新用户或者愿意升级的用户可以使用v2版本。

更多…

欲了解更多信息,参考:

错误处理

有时你希望自定义默认的错误处理格式。例如,我们需要知道响应时间戳,以及响应是否是成功的。框架提供了一个很简单的方式来做到这点。

准备

重复创建一个REST服务器小节中准备如何做…的所有步骤。

如何做…

为了达到这个目标,你可以响应@app/config/web.php中的respond to the beforeSend event of the response组件:

  1. 'response' => [
  2. 'class' => 'yii\web\Response',
  3. 'on beforeSend' => function ($event) {
  4. $response = $event->sender;
  5. if ($response->data !== null) {
  6. $response->data = [
  7. 'success' => $response->isSuccessful,
  8. 'timestamp' => time(),
  9. 'path' => Yii::$app->request->getPathInfo(),
  10. 'data' => $response->data,
  11. ];
  12. }
  13. },
  14. ],

工作原理…

为了了解代码中发生了什么,我们来试一下。首先,在控制台中运行如下代码:

  1. curl -i "http://yii-book.app/films/1"

你将得到如下输出:

  1. HTTP/1.1 200 OK
  2. Date: Thu, 24 Sep 2015 04:24:52 GMT
  3. Server: Apache
  4. X-Powered-By: PHP/5.5.23
  5. Content-Length: 115
  6. Content-Type: application/json; charset=UTF-8
  7. {"success":true,"timestamp":1443068692,"path":"films/
  8. 1","data":{"id":1,"title":"Interstellar","release_year":2014}}

然后,在控制台中运行如下代码:

  1. curl -i "http://yii-book.app/films/1000"

你将得到如下代码:

  1. HTTP/1.1 404 Not Found
  2. Date: Thu, 24 Sep 2015 04:24:26 GMT
  3. Server: Apache
  4. X-Powered-By: PHP/5.5.23
  5. Content-Length: 186
  6. Content-Type: application/json; charset=UTF-8
  7. {"success":false,"timestamp":1443068666,"path":"films/
  8. 1000","data":{"name":"Not Found","message":"Object not found:
  9. 1000","code":0,"status":404,"type":"yii\\web\\NotFoundHttpException"}
  10. }

我们在发送响应内容前做了修改。这中方法很容易定义响应是否成功。

参考

欲了解更多信息,参考http://www.yiiframework.com/doc-2.0/guide-rest-error-handling.html