Commit 063a4ebf by Qiang Xue

Merge pull request #5014 from yii2-chinesization/master

Chinese Translation for last month
parents 7ce6a8a4 82fc4365
...@@ -139,9 +139,9 @@ RESTful Web 服务 ...@@ -139,9 +139,9 @@ RESTful Web 服务
----- -----
* **编撰中** [概述](test-overview.md) * **编撰中** [概述](test-overview.md)
* **待定中** [单元测试](test-unit.md) * **编撰中** [单元测试](test-unit.md)
* **待定中** [功能测试](test-functional.md) * **编撰中** [功能测试](test-functional.md)
* **待定中** [验收测试](test-acceptance.md) * **编撰中** [验收测试](test-acceptance.md)
* **编撰中** [测试夹具](test-fixtures.md) * **编撰中** [测试夹具](test-fixtures.md)
......
...@@ -67,15 +67,15 @@ if ($data === false) { ...@@ -67,15 +67,15 @@ if ($data === false) {
Yii 支持一系列缓存存储器,概况如下: Yii 支持一系列缓存存储器,概况如下:
* [[yii\caching\ApcCache]]: 使用 PHP [APC](http://php.net/manual/en/book.apc.php) 扩展。这个选项可以认为是集中式应用程序环境中(例如:单一服务器,没有独立的负载均衡器等)最快的缓存方案。 * [[yii\caching\ApcCache]]使用 PHP [APC](http://php.net/manual/en/book.apc.php) 扩展。这个选项可以认为是集中式应用程序环境中(例如:单一服务器,没有独立的负载均衡器等)最快的缓存方案。
* [[yii\caching\DbCache]]: 使用一个数据库的表存储缓存数据。要使用这个缓存,你必须创建一个与 [[yii\caching\DbCache::cacheTable]] 对应的表。 * [[yii\caching\DbCache]]使用一个数据库的表存储缓存数据。要使用这个缓存,你必须创建一个与 [[yii\caching\DbCache::cacheTable]] 对应的表。
* [[yii\caching\DummyCache]]: 仅作为一个缓存占位符,不实现任何真正的缓存功能。这个组件的目的是为了简化那些需要查询缓存有效性的代码。例如,在开发中如果服务器没有实际的缓存支持,用它配置一个缓存组件。一个真正的缓存服务启用后,可以再切换为使用相应的缓存组件。两种条件下你都可以使用同样的代码 `Yii::$app->cache->get($key)` 尝试从缓存中取回数据而不用担心 `Yii::$app->cache` 可能是 `null` * [[yii\caching\DummyCache]]: 仅作为一个缓存占位符,不实现任何真正的缓存功能。这个组件的目的是为了简化那些需要查询缓存有效性的代码。例如,在开发中如果服务器没有实际的缓存支持,用它配置一个缓存组件。一个真正的缓存服务启用后,可以再切换为使用相应的缓存组件。两种条件下你都可以使用同样的代码 `Yii::$app->cache->get($key)` 尝试从缓存中取回数据而不用担心 `Yii::$app->cache` 可能是 `null`
* [[yii\caching\FileCache]]: 使用标准文件存储缓存数据。这个特别适用于缓存大块数据,例如一个整页的内容。 * [[yii\caching\FileCache]]使用标准文件存储缓存数据。这个特别适用于缓存大块数据,例如一个整页的内容。
* [[yii\caching\MemCache]]: 使用 PHP [memcache](http://php.net/manual/en/book.memcache.php)[memcached](http://php.net/manual/en/book.memcached.php) 扩展。这个选项被看作分布式应用环境中(例如:多台服务器,有负载均衡等)最快的缓存方案。 * [[yii\caching\MemCache]]使用 PHP [memcache](http://php.net/manual/en/book.memcache.php)[memcached](http://php.net/manual/en/book.memcached.php) 扩展。这个选项被看作分布式应用环境中(例如:多台服务器,有负载均衡等)最快的缓存方案。
* [[yii\redis\Cache]]: 实现了一个基于 [Redis](http://redis.io/) 键值对存储器的缓存组件(需要 redis 2.6.12 及以上版本的支持 )。 * [[yii\redis\Cache]]实现了一个基于 [Redis](http://redis.io/) 键值对存储器的缓存组件(需要 redis 2.6.12 及以上版本的支持 )。
* [[yii\caching\WinCache]]: 使用 PHP [WinCache](http://iis.net/downloads/microsoft/wincache-extension)[另可参考](http://php.net/manual/en/book.wincache.php))扩展. * [[yii\caching\WinCache]]使用 PHP [WinCache](http://iis.net/downloads/microsoft/wincache-extension)[另可参考](http://php.net/manual/en/book.wincache.php))扩展.
* [[yii\caching\XCache]]: 使用 PHP [XCache](http://xcache.lighttpd.net/)扩展。 * [[yii\caching\XCache]]使用 PHP [XCache](http://xcache.lighttpd.net/)扩展。
* [[yii\caching\ZendDataCache]]: 使用 [Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm) 作为底层缓存媒介。 * [[yii\caching\ZendDataCache]]使用 [Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm) 作为底层缓存媒介。
> Tip: 你可以在同一个应用程序中使用不同的缓存存储器。一个常见的策略是使用基于内存的缓存存储器存储小而常用的数据(例如:统计数据),使用基于文件或数据库的缓存存储器存储大而不太常用的数据(例如:网页内容)。 > Tip: 你可以在同一个应用程序中使用不同的缓存存储器。一个常见的策略是使用基于内存的缓存存储器存储小而常用的数据(例如:统计数据),使用基于文件或数据库的缓存存储器存储大而不太常用的数据(例如:网页内容)。
...@@ -85,15 +85,15 @@ Yii 支持一系列缓存存储器,概况如下: ...@@ -85,15 +85,15 @@ Yii 支持一系列缓存存储器,概况如下:
所有缓存组件都有同样的基类 [[yii\caching\Cache]] ,因此都支持如下 API: 所有缓存组件都有同样的基类 [[yii\caching\Cache]] ,因此都支持如下 API:
* [[yii\caching\Cache::get()|get()]]: 通过一个指定的键(key)从缓存中取回一项数据。如果该项数据不存在于缓存中或者已经过期/失效,则返回值 false。 * [[yii\caching\Cache::get()|get()]]通过一个指定的键(key)从缓存中取回一项数据。如果该项数据不存在于缓存中或者已经过期/失效,则返回值 false。
* [[yii\caching\Cache::set()|set()]]: 将一项数据指定一个键,存放到缓存中。 * [[yii\caching\Cache::set()|set()]]将一项数据指定一个键,存放到缓存中。
* [[yii\caching\Cache::add()|add()]]: 如果缓存中未找到该键,则将指定数据存放到缓存中。 * [[yii\caching\Cache::add()|add()]]如果缓存中未找到该键,则将指定数据存放到缓存中。
* [[yii\caching\Cache::mget()|mget()]]: 通过指定的多个键从缓存中取回多项数据。 * [[yii\caching\Cache::mget()|mget()]]通过指定的多个键从缓存中取回多项数据。
* [[yii\caching\Cache::mset()|mset()]]: 将多项数据存储到缓存中,每项数据对应一个键。 * [[yii\caching\Cache::mset()|mset()]]将多项数据存储到缓存中,每项数据对应一个键。
* [[yii\caching\Cache::madd()|madd()]]: 将多项数据存储到缓存中,每项数据对应一个键。如果某个键已经存在于缓存中,则该项数据会被跳过。 * [[yii\caching\Cache::madd()|madd()]]将多项数据存储到缓存中,每项数据对应一个键。如果某个键已经存在于缓存中,则该项数据会被跳过。
* [[yii\caching\Cache::exists()|exists()]]: 返回一个值,指明某个键是否存在于缓存中。 * [[yii\caching\Cache::exists()|exists()]]返回一个值,指明某个键是否存在于缓存中。
* [[yii\caching\Cache::delete()|delete()]]: 通过一个键,删除缓存中对应的值。 * [[yii\caching\Cache::delete()|delete()]]通过一个键,删除缓存中对应的值。
* [[yii\caching\Cache::flush()|flush()]]: 删除缓存中的所有数据。 * [[yii\caching\Cache::flush()|flush()]]删除缓存中的所有数据。
有些缓存存储器如 MemCache,APC 支持以批量模式取回缓存值,这样可以节省取回缓存数据的开支。 [[yii\caching\Cache::mget()|mget()]] 和 [[yii\caching\Cache::madd()|madd()]] API提供对该特性的支持。如果底层缓存存储器不支持该特性,Yii 也会模拟实现。 有些缓存存储器如 MemCache,APC 支持以批量模式取回缓存值,这样可以节省取回缓存数据的开支。 [[yii\caching\Cache::mget()|mget()]] 和 [[yii\caching\Cache::madd()|madd()]] API提供对该特性的支持。如果底层缓存存储器不支持该特性,Yii 也会模拟实现。
...@@ -177,12 +177,11 @@ $data = $cache->get($key); ...@@ -177,12 +177,11 @@ $data = $cache->get($key);
下面是可用的缓存依赖的概况: 下面是可用的缓存依赖的概况:
- [[yii\caching\ChainedDependency]]: 如果依赖链上任何一个依赖产生变化,则依赖改变。 - [[yii\caching\ChainedDependency]]:如果依赖链上任何一个依赖产生变化,则依赖改变。
- [[yii\caching\DbDependency]]: 如果指定 SQL 语句的查询结果发生了变化,则依赖改变。 - [[yii\caching\DbDependency]]:如果指定 SQL 语句的查询结果发生了变化,则依赖改变。
- [[yii\caching\ExpressionDependency]]: 如果指定的 PHP 表达式执行结果发生变化,则依赖改变。 - [[yii\caching\ExpressionDependency]]:如果指定的 PHP 表达式执行结果发生变化,则依赖改变。
- [[yii\caching\FileDependency]]: 如果文件的最后修改时间发生变化,则依赖改变。 - [[yii\caching\FileDependency]]:如果文件的最后修改时间发生变化,则依赖改变。
- [[yii\caching\TagDependency]]: 为一项缓存数据添加一个或多个标签。你可以通过调用 [[yii\caching\TagDependency::invalidate()]] - [[yii\caching\GroupDependency]]:将一项缓存数据标记到一个组名,你可以通过调用 [[yii\caching\GroupDependency::invalidate()]] 一次性将相同组名的缓存全部置为失效状态。
一次性将具有指定标签的缓存数据全部置为失效状态。
## 查询缓存 <a name="query-caching"></a> ## 查询缓存 <a name="query-caching"></a>
......
...@@ -19,7 +19,7 @@ Yii::setAlias('@bar', 'http://www.example.com'); ...@@ -19,7 +19,7 @@ Yii::setAlias('@bar', 'http://www.example.com');
> 注意:别名所指向的文件路径或 URL 不一定是真实存在的文件或资源。 > 注意:别名所指向的文件路径或 URL 不一定是真实存在的文件或资源。
可以通过在一个别名后面加斜杠 `/` 和一至多个路径分段生成新别名(无需调用 [[Yii::setAlias()]])。我们把通过 [[Yii::setAlias()]] 定义的别名称为**根别名**,而用他们衍生出去的别名成为**衍生别名**。例如,`@foo` 就是别名,而 `@foo/bar/file.php` 是一个衍生别名。 可以通过在一个别名后面加斜杠 `/` 和一至多个路径分段生成新别名(无需调用 [[Yii::setAlias()]])。我们把通过 [[Yii::setAlias()]] 定义的别名称为**根别名**,而用他们衍生出去的别名成为**衍生别名**。例如,`@foo` 就是别名,而 `@foo/bar/file.php` 是一个衍生别名。
你还可以用别名去定义新别名(根别名与衍生别名均可): 你还可以用别名去定义新别名(根别名与衍生别名均可):
...@@ -86,7 +86,7 @@ $cache = new FileCache([ ...@@ -86,7 +86,7 @@ $cache = new FileCache([
预定义的别名 <a name="predefined-aliases"></a> 预定义的别名 <a name="predefined-aliases"></a>
------------------ ------------------
Yii 预定义了一系列别名来简化常用路径和 URL的使用: Yii 预定义了一系列别名来简化常用路径和 URL 的使用:
- `@yii` - `BaseYii.php` 文件所在的目录(也被称为框架安装目录) - `@yii` - `BaseYii.php` 文件所在的目录(也被称为框架安装目录)
- `@app` - 当前运行的应用 [[yii\base\Application::basePath|根路径(base path)]] - `@app` - 当前运行的应用 [[yii\base\Application::basePath|根路径(base path)]]
......
错误处理
==============
处理一个 RESTful API 请求时, 如果有一个用户请求错误或服务器发生意外时, 你可以简单地抛出一个异常来通知用户出错了。
如果你能找出错误的原因 (例如,所请求的资源不存在),你应该
考虑抛出一个适当的HTTP状态代码的异常 (例如, [[yii\web\NotFoundHttpException]]
意味着一个404 HTTP状态代码)。 Yii 将通过HTTP状态码和文本
发送相应的响应。 它还将包括在响应主体异常的
序列化表示形式。 例如,
```
HTTP/1.1 404 Not Found
Date: Sun, 02 Mar 2014 05:31:43 GMT
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8
{
"type": "yii\\web\\NotFoundHttpException",
"name": "Not Found Exception",
"message": "The requested resource was not found.",
"code": 0,
"status": 404
}
```
下面的列表总结了Yii的REST框架的HTTP状态代码:
* `200`: OK。一切正常。
* `201`: 响应 `POST` 请求时成功创建一个资源。`Location` header
包含的URL指向新创建的资源。
* `204`: 该请求被成功处理,响应不包含正文内容 (类似 `DELETE` 请求)。
* `304`: 资源没有被修改。可以使用缓存的版本。
* `400`: 错误的请求。可能通过用户方面的多种原因引起的,例如在请求体内有无效的JSON
数据,无效的操作参数,等等。
* `401`: 验证失败。
* `403`: 已经经过身份验证的用户不允许访问指定的 API 末端。
* `404`: 所请求的资源不存在。
* `405`: 不被允许的方法。 请检查 `Allow` header 允许的HTTP方法。
* `415`: 不支持的媒体类型。 所请求的内容类型或版本号是无效的。
* `422`: 数据验证失败 (例如,响应一个 `POST` 请求)。 请检查响应体内详细的错误消息。
* `429`: 请求过多。 由于限速请求被拒绝。
* `500`: 内部服务器错误。 这可能是由于内部程序错误引起的。
快速入门
===========
Yii 提供了一整套用来简化实现RESTful风格的Web Service服务的API。
特别是,Yii支持以下关于RESTful风格的API:
* 支持 [Active Record](db-active-record.md) 类的通用API的快速原型;
* 涉及的响应格式(在默认情况下支持JSON 和 XML);
* 支持可选输出字段的 可定制对象序列化;
* 适当的格式的数据采集和验证错误;
* 支持 [HATEOAS](http://en.wikipedia.org/wiki/HATEOAS);
* 有适当HTTP动词检查的高效的路由;
* 内置`OPTIONS``HEAD`动词的支持;
* 认证和授权;
* 数据缓存和HTTP缓存;
* 速率限制;
如下, 我们用一个例子来说明如何用最少的编码来建立一套RESTful风格的API。
假设你想通过RESTful风格的API来展示用户数据。用户数据被存储在用户DB表,
你已经创建了 [[yii\db\ActiveRecord|ActiveRecord]] 类 `app\models\User` 来访问该用户数据.
## 创建一个控制器 <a name="creating-controller"></a>
首先,创建一个控制器类 `app\controllers\UserController` 如下,
```php
namespace app\controllers;
use yii\rest\ActiveController;
class UserController extends ActiveController
{
public $modelClass = 'app\models\User';
}
```
控制器类扩展自 [[yii\rest\ActiveController]]。通过指定 [[yii\rest\ActiveController::modelClass|modelClass]]
作为 `app\models\User`, 控制器就能知道使用哪个模型去获取和处理数据。
## 配置URL规则 <a name="configuring-url-rules"></a>
然后,修改有关在应用程序配置的`urlManager`组件的配置:
```php
'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
['class' => 'yii\rest\UrlRule', 'controller' => 'user'],
],
]
```
上面的配置主要是为`user`控制器增加一个URL规则。这样,
用户的数据就能通过美化的URL和有意义的http动词进行访问和操作。
## 尝试 <a name="trying-it-out"></a>
随着以上所做的最小的努力,你已经完成了创建用于访问用户数据
的RESTful风格的API。您所创建的API包括:
* `GET /users`: 逐页列出所有用户;
* `HEAD /users`: 显示用户列表的概要信息;
* `POST /users`: 创建一个新用户;
* `GET /users/123`: 返回用户为123的详细信息;
* `HEAD /users/123`: 显示用户 123 的概述信息;
* `PATCH /users/123` and `PUT /users/123`: 更新用户123;
* `DELETE /users/123`: 删除用户123;
* `OPTIONS /users`: 显示关于末端 `/users` 支持的动词;
* `OPTIONS /users/123`: 显示有关末端 `/users/123` 支持的动词。
> Info: Yii将在末端使用的控制器的名称自动变为复数。
你可以访问你的API用`curl`命令如下,
```
$ curl -i -H "Accept:application/json" "http://localhost/users"
HTTP/1.1 200 OK
Date: Sun, 02 Mar 2014 05:31:43 GMT
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
X-Powered-By: PHP/5.4.20
X-Pagination-Total-Count: 1000
X-Pagination-Page-Count: 50
X-Pagination-Current-Page: 1
X-Pagination-Per-Page: 20
Link: <http://localhost/users?page=1>; rel=self,
<http://localhost/users?page=2>; rel=next,
<http://localhost/users?page=50>; rel=last
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8
[
{
"id": 1,
...
},
{
"id": 2,
...
},
...
]
```
试着改变可接受的内容类型为`application/xml`,你会看到结果以XML格式返回:
```
$ curl -i -H "Accept:application/xml" "http://localhost/users"
HTTP/1.1 200 OK
Date: Sun, 02 Mar 2014 05:31:43 GMT
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
X-Powered-By: PHP/5.4.20
X-Pagination-Total-Count: 1000
X-Pagination-Page-Count: 50
X-Pagination-Current-Page: 1
X-Pagination-Per-Page: 20
Link: <http://localhost/users?page=1>; rel=self,
<http://localhost/users?page=2>; rel=next,
<http://localhost/users?page=50>; rel=last
Transfer-Encoding: chunked
Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<response>
<item>
<id>1</id>
...
</item>
<item>
<id>2</id>
...
</item>
...
</response>
```
> Tip: 您还可以通过Web浏览器中输入URL `http://localhost/users` 来访问你的API。
尽管如此,你可能需要一些浏览器插件来发送特定的headers请求。
如你所见, 在headers响应, 有关于总数,页数的信息,等等。
还有一些链接,让您导航到其他页面的数据. 例如, `http://localhost/users?page=2`
会给你的用户数据的下一个页面。
使用 `fields``expand` 参数, 您也可以指定哪些字段应该包含在结果内。
例如, URL `http://localhost/users?fields=id,email` 将只返回 `id``email` 字段。
> Info: 您可能已经注意到了 `http://localhost/users` 的结果包括一些敏感字段,
> 例如 `password_hash`, `auth_key`。 你肯定不希望这些出现在你的API结果中。
> 你应该在 [Response Formatting](rest-response-formatting.md) 部分中过滤掉这些字段。
## 总结 <a name="summary"></a>
使用Yii框架的RESTful风格的API, 在控制器的操作中实现API末端, 使用
控制器来组织末端接口为一个单一的资源类型。
[[yii\base\Model]] 类扩展的资源被表示为数据模型。
如果你在使用(关系或非关系)数据库,推荐您使用 [[yii\db\ActiveRecord|ActiveRecord]]
来表示资源。
你可以使用 [[yii\rest\UrlRule]] 简化路由到你的API末端。
虽然不是必须的,为了方便维护您的WEB前端和后端,
建议您开发接口作为一个单独的应用程序。
速率限制
=============
为防止滥用,你应该考虑增加速率限制到您的API。
例如,您可以限制每个用户的API的使用是在10分钟内最多100次的API调用。
如果一个用户同一个时间段内太多的请求被接收, 将返回响应状态代码 429 (这意味着过多的请求)。
要启用速率限制, [[yii\web\User::identityClass|user identity class]] 应该实现 [[yii\filters\RateLimitInterface]].
这个接口需要实现以下三个方法:
* `getRateLimit()`: 返回允许的请求的最大数目及时间,例如,`[100, 600]` 表示在600秒内最多100次的API调用。
* `loadAllowance()`: 返回剩余的允许的请求和相应的UNIX时间戳数
当最后一次速率限制检查时。
* `saveAllowance()`: 保存允许剩余的请求数和当前的UNIX时间戳。
你可以在user表中使用两列来记录容差和时间戳信息。
`loadAllowance()``saveAllowance()` 可以通过实现对符合当前身份验证的用户
的这两列值的读和保存。为了提高性能,你也可以
考虑使用缓存或NoSQL存储这些信息。
一旦 identity 实现所需的接口, Yii 会自动使用 [[yii\filters\RateLimiter]]
[[yii\rest\Controller]] 配置一个行为过滤器来执行速率限制检查。 如果速度超出限制
该速率限制器将抛出一个 [[yii\web\TooManyRequestsHttpException]]。 你可以在你的 REST
控制器类里配置速率限制,
```php
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['rateLimiter']['enableRateLimitHeaders'] = false;
return $behaviors;
}
```
当速率限制被激活,默认情况下每个响应将包含以下HTTP头发送
目前的速率限制信息:
* `X-Rate-Limit-Limit`: 同一个时间段所允许的请求的最大数目;
* `X-Rate-Limit-Remaining`: 在当前时间段内剩余的请求的数量;
* `X-Rate-Limit-Reset`: 为了得到最大请求数所等待的秒数。
你可以禁用这些头信息通过配置 [[yii\filters\RateLimiter::enableRateLimitHeaders]] 为false,
就像在上面的代码示例所示。
响应格式
===================
当处理一个 RESTful API 请求时, 一个应用程序通常需要如下步骤
来处理响应格式:
1. 确定可能影响响应格式的各种因素, 例如媒介类型, 语言, 版本, 等等。
这个过程也被称为 [content negotiation](http://en.wikipedia.org/wiki/Content_negotiation)
2. 资源对象转换为数组, 如在 [Resources](rest-resources.md) 部分中所描述的。
通过 [[yii\rest\Serializer]] 来完成。
3. 通过内容协商步骤将数组转换成字符串。
[yii\web\ResponseFormatterInterface|response formatters]] 通过
[yii\web\Response::formatters|response]] 应用程序组件来注册完成。
## 内容协商 <a name="content-negotiation"></a>
Yii 提供了通过 [[yii\filters\ContentNegotiator]] 过滤器支持内容协商。RESTful API 基于
控制器类 [[yii\rest\Controller]] 在 `contentNegotiator` 下配备这个过滤器。
文件管理器提供了涉及的响应格式和语言。 例如, 如果一个 RESTful
API 请求中包含以下 header,
```
Accept: application/json; q=1.0, */*; q=0.1
```
将会得到JSON格式的响应,如下:
```
$ curl -i -H "Accept: application/json; q=1.0, */*; q=0.1" "http://localhost/users"
HTTP/1.1 200 OK
Date: Sun, 02 Mar 2014 05:31:43 GMT
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
X-Powered-By: PHP/5.4.20
X-Pagination-Total-Count: 1000
X-Pagination-Page-Count: 50
X-Pagination-Current-Page: 1
X-Pagination-Per-Page: 20
Link: <http://localhost/users?page=1>; rel=self,
<http://localhost/users?page=2>; rel=next,
<http://localhost/users?page=50>; rel=last
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8
[
{
"id": 1,
...
},
{
"id": 2,
...
},
...
]
```
幕后,执行一个 RESTful API 控制器动作之前,[[yii\filters\ContentNegotiator]]
filter 将检查 `Accept` HTTP header 在请求时和配置 [[yii\web\Response::format|response format]]
`'json'`。 之后的动作被执行并返回得到的资源对象或集合,
[[yii\rest\Serializer]] 将结果转换成一个数组。最后,[[yii\web\JsonResponseFormatter]]
该数组将序列化为JSON字符串,并将其包括在响应主体。
默认, RESTful APIs 同时支持JSON和XML格式。为了支持新的格式,你应该
`contentNegotiator` 过滤器中配置 [[yii\filters\ContentNegotiator::formats|formats]] 属性,
类似如下 API 控制器类:
```php
use yii\web\Response;
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_HTML;
return $behaviors;
}
```
`formats` 属性的keys支持 MIME 类型,而 values 必须在 [[yii\web\Response::formatters]]
中支持被响应格式名称。
## 数据序列化 <a name="data-serializing"></a>
正如我们上面所描述的,[[yii\rest\Serializer]] 负责转换资源的中间件
对象或集合到数组。它将对象 [[yii\base\ArrayableInterface]] 作为
[[yii\data\DataProviderInterface]]。 前者主要由资源对象实现, 而
后者是资源集合。
你可以通过设置 [[yii\rest\Controller::serializer]] 属性和一个配置数组。
例如,有时你可能想通过直接在响应主体内包含分页信息来
简化客户端的开发工作。这样做,按照如下规则配置 [[yii\rest\Serializer::collectionEnvelope]]
属性:
```php
use yii\rest\ActiveController;
class UserController extends ActiveController
{
public $modelClass = 'app\models\User';
public $serializer = [
'class' => 'yii\rest\Serializer',
'collectionEnvelope' => 'items',
];
}
```
那么你的请求可能会得到的响应如下 `http://localhost/users`:
```
HTTP/1.1 200 OK
Date: Sun, 02 Mar 2014 05:31:43 GMT
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
X-Powered-By: PHP/5.4.20
X-Pagination-Total-Count: 1000
X-Pagination-Page-Count: 50
X-Pagination-Current-Page: 1
X-Pagination-Per-Page: 20
Link: <http://localhost/users?page=1>; rel=self,
<http://localhost/users?page=2>; rel=next,
<http://localhost/users?page=50>; rel=last
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8
{
"items": [
{
"id": 1,
...
},
{
"id": 2,
...
},
...
],
"_links": {
"self": "http://localhost/users?page=1",
"next": "http://localhost/users?page=2",
"last": "http://localhost/users?page=50"
},
"_meta": {
"totalCount": 1000,
"pageCount": 50,
"currentPage": 1,
"perPage": 20
}
}
```
路由
=======
随着资源和控制器类准备,您可以使用URL如
`http://localhost/index.php?r=user/create`访问资源,类似于你可以用正常的Web应用程序做法。
在实践中,你通常要用美观的URL并采取有优势的HTTP动词。
例如,请求`POST /users`意味着访问`user/create`动作。
这可以很容易地通过配置`urlManager`应用程序组件来完成
如下所示:
```php
'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
['class' => 'yii\rest\UrlRule', 'controller' => 'user'],
],
]
```
相比于URL管理的Web应用程序,上述主要的新东西是通过RESTful API
请求[[yii\rest\UrlRule]]。这个特殊的URL规则类将会
建立一整套子URL规则来支持路由和URL创建的指定的控制器。
例如, 上面的代码中是大致按照下面的规则:
```php
[
'PUT,PATCH users/<id>' => 'user/update',
'DELETE users/<id>' => 'user/delete',
'GET,HEAD users/<id>' => 'user/view',
'POST users' => 'user/create',
'GET,HEAD users' => 'user/index',
'users/<id>' => 'user/options',
'users' => 'user/options',
]
```
该规则支持下面的API末端:
* `GET /users`: 逐页列出所有用户;
* `HEAD /users`: 显示用户列表的概要信息;
* `POST /users`: 创建一个新用户;
* `GET /users/123`: 返回用户为123的详细信息;
* `HEAD /users/123`: 显示用户 123 的概述信息;
* `PATCH /users/123` and `PUT /users/123`: 更新用户123;
* `DELETE /users/123`: 删除用户123;
* `OPTIONS /users`: 显示关于末端 `/users` 支持的动词;
* `OPTIONS /users/123`: 显示有关末端 `/users/123` 支持的动词。
您可以通过配置 `only``except` 选项来明确列出哪些行为支持,
哪些行为禁用。例如,
```php
[
'class' => 'yii\rest\UrlRule',
'controller' => 'user',
'except' => ['delete', 'create', 'update'],
],
```
您也可以通过配置 `patterns``extraPatterns` 重新定义现有的模式或添加此规则支持的新模式。
例如,通过末端 `GET /users/search` 可以支持新行为 `search`, 按照如下配置 `extraPatterns` 选项,
```php
[
'class' => 'yii\rest\UrlRule',
'controller' => 'user',
'extraPatterns' => [
'GET search' => 'search',
],
```
您可能已经注意到控制器ID`user`以复数形式出现在`users`末端。
这是因为 [[yii\rest\UrlRule]] 能够为他们使用的末端全自动复数化控制器ID。
您可以通过设置 [[yii\rest\UrlRule::pluralize]] 为false 来禁用此行为,如果您想
使用一些特殊的名字您可以通过配置 [[yii\rest\UrlRule::controller]] 属性。
版本
==========
你的API应该是版本化的。不像你完全控制在客户端和服务器端Web应用程序代码, 对于API,您通常没有对API的客户端代码的控制权。
因此,应该尽可能的保持向后兼容性(BC),如果一些不能向后兼容的变化必须引入
APIs,你应该增加版本号。你可以参考[Semantic Versioning](http://semver.org/)
有关设计的API的版本号的详细信息。
关于如何实现API版本,一个常见的做法是在API的URL中嵌入版本号。
例如,`http://example.com/v1/users`代表`/users`版本1的API. 另一种API版本化的方法最近用的非常多的是把版本号放入HTTP请求头,通常是通过`Accept`头,
如下:
```
// 通过参数
Accept: application/json; version=v1
// 通过vendor的内容类型
Accept: application/vnd.company.myapp-v1+json
```
这两种方法都有优点和缺点, 而且关于他们也有很多争论。
下面我们描述在一种API版本混合了这两种方法的一个实用的策略:
* 把每个主要版本的API实现在一个单独的模块ID的主版本号 (例如 `v1`, `v2`)。
自然,API的url将包含主要的版本号。
* 在每一个主要版本 (在相应的模块),使用 `Accept` HTTP 请求头
确定小版本号编写条件代码来响应相应的次要版本.
为每个模块提供一个主要版本, 它应该包括资源类和控制器类
为特定服务版本。 更好的分离代码, 你可以保存一组通用的
基础资源和控制器类, 并用在每个子类版本模块。 在子类中,
实现具体的代码例如 `Model::fields()`
你的代码可以类似于如下的方法组织起来:
```
api/
common/
controllers/
UserController.php
PostController.php
models/
User.php
Post.php
modules/
v1/
controllers/
UserController.php
PostController.php
models/
User.php
Post.php
v2/
controllers/
UserController.php
PostController.php
models/
User.php
Post.php
```
你的应用程序配置应该这样:
```php
return [
'modules' => [
'v1' => [
'basePath' => '@app/modules/v1',
],
'v2' => [
'basePath' => '@app/modules/v2',
],
],
'components' => [
'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
['class' => 'yii\rest\UrlRule', 'controller' => ['v1/user', 'v1/post']],
['class' => 'yii\rest\UrlRule', 'controller' => ['v2/user', 'v2/post']],
],
],
],
];
```
因此,`http://example.com/v1/users`将返回版本1的用户列表,而
`http://example.com/v2/users`将返回版本2的用户。
使用模块, 将不同版本的代码隔离。 通过共用基类和其他类
跨模块重用代码也是有可能的。
为了处理次要版本号, 可以利用内容协商
功能通过 [[yii\filters\ContentNegotiator|contentNegotiator]] 提供的行为。`contentNegotiator`
行为可设置 [[yii\web\Response::acceptParams]] 属性当它确定
支持哪些内容类型时。
例如, 如果一个请求通过 `Accept: application/json; version=v1`被发送,
内容交涉后,[[yii\web\Response::acceptParams]]将包含值`['version' => 'v1']`.
基于 `acceptParams` 的版本信息,你可以写条件代码
如 actions,resource classes,serializers等等。
由于次要版本需要保持向后兼容性,希望你的代码不会有
太多的版本检查。否则,有机会你可能需要创建一个新的主要版本。
...@@ -27,16 +27,16 @@ CREATE TABLE `country` ( ...@@ -27,16 +27,16 @@ CREATE TABLE `country` (
`population` INT(11) NOT NULL DEFAULT '0' `population` INT(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `Country` VALUES ('AU','Australia',18886000); INSERT INTO `country` VALUES ('AU','Australia',18886000);
INSERT INTO `Country` VALUES ('BR','Brazil',170115000); INSERT INTO `country` VALUES ('BR','Brazil',170115000);
INSERT INTO `Country` VALUES ('CA','Canada',1147000); INSERT INTO `country` VALUES ('CA','Canada',1147000);
INSERT INTO `Country` VALUES ('CN','China',1277558000); INSERT INTO `country` VALUES ('CN','China',1277558000);
INSERT INTO `Country` VALUES ('DE','Germany',82164700); INSERT INTO `country` VALUES ('DE','Germany',82164700);
INSERT INTO `Country` VALUES ('FR','France',59225700); INSERT INTO `country` VALUES ('FR','France',59225700);
INSERT INTO `Country` VALUES ('GB','United Kingdom',59623400); INSERT INTO `country` VALUES ('GB','United Kingdom',59623400);
INSERT INTO `Country` VALUES ('IN','India',1013662000); INSERT INTO `country` VALUES ('IN','India',1013662000);
INSERT INTO `Country` VALUES ('RU','Russia',146934000); INSERT INTO `country` VALUES ('RU','Russia',146934000);
INSERT INTO `Country` VALUES ('US','United States',278357000); INSERT INTO `country` VALUES ('US','United States',278357000);
``` ```
于是便有了一个名为 `yii2basic` 的数据库,在这个数据库中有一个包含三个字段的数据表 `country`,表中有十行数据。 于是便有了一个名为 `yii2basic` 的数据库,在这个数据库中有一个包含三个字段的数据表 `country`,表中有十行数据。
......
运行应用 运行应用
==================== ====================
安装 Yii 后,就有了一个运行中的 Yii 应用,根据配置的不同,可以通过 `http://hostname/basic/web/index.php``http://hostname/index.php` 访问。本章节将介绍应用的内建功能,如何组织代码,以及一般情况下应用如何处理请求。 安装 Yii 后,就有了一个可运行的 Yii 应用,根据配置的不同,可以通过 `http://hostname/basic/web/index.php``http://hostname/index.php` 访问。本章节将介绍应用的内建功能,如何组织代码,以及一般情况下应用如何处理请求。
> 补充:为简单起见,在整个“入门”板块都假定你已经把 `basic/web` 设为 Web 服务器根目录并配置完毕,你访问应用的地址会是 `http://lostname/index.php` 或类似的。请按需调整 URL。 > 补充:为简单起见,在整个“入门”板块都假定你已经把 `basic/web` 设为 Web 服务器根目录并配置完毕,你访问应用的地址会是 `http://lostname/index.php` 或类似的。请按需调整 URL。
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
--------------------- ---------------------
应用中最重要的目录和文件(假设应用根目录是 `basic`): 应用中最重要的目录和文件(假设应用根目录是 `basic`):
``` ```
basic/ 应用根目录 basic/ 应用根目录
composer.json Composer 配置文件, 描述包信息 composer.json Composer 配置文件, 描述包信息
......
...@@ -121,7 +121,7 @@ public function rules() ...@@ -121,7 +121,7 @@ public function rules()
```php ```php
function foo($model, $attribute) { function foo($model, $attribute) {
// ... compute $value ... // ... 计算 $value ...
return $value; return $value;
} }
``` ```
......
引入第三方代码
=============================
有时,你可能会需要在 Yii 应用中使用第三方的代码。又或者是你想要在第三方系统中把 Yii 作为类库引用。在下面这个板块中,我们向你展示如何实现这些目标。
## 在 Yii 中使用第三方类库 <a name="using-libs-in-yii"></a>
要想在 Yii 应用中使用第三方类库,你主要需要确保这些库中的类文件都可以被正常导入或可以被自动加载。
### 使用 Composer 包 <a name="using-composer-packages"></a>
目前很多第三方的类库都以 [Composer](https://getcomposer.org/) 包的形式发布。你只需要以下两个简单的步骤即可安装他们:
1. 修改你应用的 `composer.json` 文件,并注明需要安装哪些 Composer 包。
2. 运行 `php composer.phar install` 安装这些包。
这些Composer 包内的类库,可以通过 Composer 的自动加载器实现自动加载。不过请确保你应用的
[入口脚本](structure-entry-scripts.md)包含以下几行用于加载 Composer 自动加载器的代码:
```php
// install Composer autoloader (安装 Composer 自动加载器)
require(__DIR__ . '/../vendor/autoload.php');
// include Yii class file (加载 Yii 的类文件)
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
```
### 使用下载的类库 <a name="using-downloaded-libs"></a>
若你的类库并未发布为一个 Composer 包,你可以参考以下安装说明来安装它。在大多数情况下,你需要预先下载一个发布文件,并把它解压缩到
`BasePath/vendor` 目录,这里的 `BasePath` 代指你应用程序自身的 [base path(主目录)](structure-applications.md#basePath)
若该类库包含他自己的类自动加载器,你可以把它安装到你应用的[入口脚本](structure-entry-scripts.md)里。我们推荐你把它的安装代码置于
`Yii.php` 的导入之前,这样 Yii 的官方自动加载器可以拥有更高的优先级。
若一个类库并没有提供自动加载器,但是他的类库命名方式符合 [PSR-4](http://www.php-fig.org/psr/psr-4/) 标准,你可以使用 Yii 官方的自动加载器来自动加载这些类。你只需给他们的每个根命名空间声明一下[根路径别名](concept-aliases.md#defining-aliases)。比如,假设说你已经在目录 `vendor/foo/bar` 里安装了一个类库,且这些类库的根命名空间为 `xyz`。你可以把以下代码放入你的应用配置文件中:
```php
[
'aliases' => [
'@xyz' => '@vendor/foo/bar',
],
]
```
若以上情形都不符合,最可能是这些类库需要依赖于 PHP 的 include_path 配置,来正确定位并导入类文件。只需参考它的安装说明简单地配置一下 PHP 导入路径即可。
最悲催的情形是,该类库需要显式导入每个类文件,你可以使用以下方法按需导入相关类文件:
* 找出该库内包含哪些类。
* 在应用的[入口脚本](structure-entry-scripts.md)里的 `Yii::$classMap` 数组中列出这些类,和他们各自对应的文件路径。
举例来说,
```php
Yii::$classMap['Class1'] = 'path/to/Class1.php';
Yii::$classMap['Class2'] = 'path/to/Class2.php';
```
## 在第三方系统内使用 Yii <a name="using-yii-in-others"></a>
因为 Yii 提供了很多牛逼的功能,有时,你可能会想要使用它们中的一些功能用来支持开发或完善某些第三方的系统,比如:WordPress,Joomla,或是用其他 PHP 框架开发的应用程序。举两个例子吧,你可能会想念方便的 [[yii\helpers\ArrayHelper]] 类,或在第三方系统中使用
[Active Record](db-active-record.md) 活动记录功能。要实现这些目标,你只需两个步骤:安装 Yii,启动 Yii。
若这个第三方系统支持 Composer 管理他的依赖文件,你可以直接运行一下命令来安装 Yii:
```
php composer.phar require yiisoft/yii2-framework:*
php composer.phar install
```
不然的话,你可以[下载](http://www.yiiframework.com/download/) Yii 的发布包,并把它解压到对应系统的 `BasePath/vendor` 目录内。
之后,你需要修改该第三方应用的入口脚本,在开头位置添加 Yii 的引入代码:
```php
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
$yiiConfig = require(__DIR__ . '/../config/yii/web.php');
new yii\web\Application($yiiConfig); // 千万别在这调用 run() 方法。(笑)
```
如你所见,这段代码与典型的 Yii 应用的[入口脚本](structure-entry-scripts.md)非常相似。唯一的不同之处在于在 Yii 应用创建成功之后,并不会紧接着调用 `run()` 方法。因为,`run()` 方法的调用会接管 HTTP 请求的处理流程。(译者注:换言之,这就不是第三方系统而是 Yii 系统了,URL 规则也会跟着换成 Yii 的规则了)
与 Yii 应用中一样,你可以依据运行该第三方系统的环境,针对性地配置 Yii 应用实例。比如,为了使用[活动记录](db-active-record.md)功能,你需要先用该第三方系统的 DB 连接信息,配置 Yii 的 `db` 应用组件。
现在,你就可以使用 Yii 提供的绝大多数功能了。比如,创建 AR 类,并用它们来操作数据库。
## 配合使用 Yii 2 和 Yii 1 <a name="using-both-yii2-yii1"></a>
如果你之前使用 Yii 1,大概你也有正在运行的 Yii 1 应用吧。不必用 Yii 2 重写整个应用,你也可以通过增添对哪些
Yii 2 独占功能的支持来增强这个系统。下面我们就来详细描述一下具体的实现过程。
> 注意:Yii 2 需要 PHP 5.4+ 的版本。你需要确保你的服务器以及现有应用都可以支持 PHP 5.4。
首先,参考前文板块中给出的方法,在已有的应用中安装 Yii 2。
之后,如下修改 Yii 1 应用的入口脚步:
```php
// 导入下面会详细说明的定制 Yii 类文件。
require(__DIR__ . '/../components/Yii.php');
// Yii 2 应用的配置文件
$yii2Config = require(__DIR__ . '/../config/yii2/web.php');
new yii\web\Application($yii2Config); // Do NOT call run()
// Yii 1 应用的配置文件
$yii1Config = require(__DIR__ . '/../config/yii1/main.php');
Yii::createWebApplication($yii1Config)->run();
```
因为,Yii 1 和 Yii 2 都包含有 `Yii` 这个类,你应该创建一个定制版的 Yii 来把他们组合起来。上面的代码里包含了的这个定制版的 `Yii` 类,可以用以下代码创建出来:
```php
$yii2path = '/path/to/yii2';
require($yii2path . '/BaseYii.php'); // Yii 2.x
$yii1path = '/path/to/yii1';
require($yii1path . '/YiiBase.php'); // Yii 1.x
class Yii extends \yii\BaseYii
{
// 复制粘贴 YiiBase (1.x) 文件中的代码于此
}
Yii::$classMap = include($yii2path . '/classes.php');
// 通过 Yii 1 注册 Yii2 的类自动加载器
Yii::registerAutoloader(['Yii', 'autoload']);
```
大功告成!此时,你可以在你代码的任意位置,调用 `Yii::$app` 以访问 Yii 2 的应用实例,而用
`Yii::app()` 则会返回 Yii 1 的应用实例:
```php
echo get_class(Yii::app()); // 输出 'CWebApplication'
echo get_class(Yii::$app); // 输出 'yii\web\Application'
```
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment