diff --git a/README.md b/README.md index 4e2e50e..b1f8779 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,13 @@ # HTTP 接口设计指北 -* 文档主要目的是为设计接口时提供建议,使大家不必重复造 HTTP 协议已经完成的轮子 -* **只是建议,不是必须遵从的要求** -* 大家有什么问题想法或者建议欢迎 [创建 Issue](https://github.com/bolasblack/http-api-guide/issues/new) 或者 [提交 Pull Request](https://github.com/bolasblack/http-api-guide/compare/) +文档主要目的是为大家在设计接口时提供建议,给大家参考 HTTP 或者其他协议/指南已经设计过的内容 + +**只是建议,不是必须遵从的要求** + +大家有什么问题想法或者建议欢迎 [创建 Issue](https://github.com/bolasblack/http-api-guide/issues/new) 或者 [提交 Pull Request](https://github.com/bolasblack/http-api-guide/compare/) + +* [README.md](.) 主要是简单介绍和列出对设计可能会有帮助的资料,少放一些私货 +* [SUPPLEMENT.md](./SUPPLEMENT.md) 有一些更细节的接口设计方面的我自己的想法,全是私货 ## 目录 @@ -12,15 +17,13 @@ * [国际化](#user-content-国际化) * [请求方法](#user-content-请求方法) * [状态码](#user-content-状态码) -* [错误处理](#user-content-错误处理) * [身份验证](#user-content-身份验证) * [超文本驱动和资源发现](#user-content-超文本驱动和资源发现) -* [分页](#user-content-分页) * [数据缓存](#user-content-数据缓存) * [并发控制](#user-content-并发控制) * [跨域](#user-content-跨域) * [其他资料](#user-content-其他资料) -* [更细节的接口设计指南](#user-content-更细节的接口设计指南) +* [其他接口设计指南](#user-content-其他接口设计指南) ## HTTP 协议 @@ -53,13 +56,14 @@ HTTP 协议的 2.0 版本还没有正式发布,但目前已经基本稳定下 ## URL -HOST 地址: +URL 的设计都需要遵守 [RFC 3986](http://tools.ietf.org/html/rfc3986) 的的规范。 - https://api.example.com +URL 的长度,在 HTTP/1.1: Message Syntax and Routing([RFC 7230](https://tools.ietf.org/html/rfc7230)) 的 [3.1.1](https://tools.ietf.org/html/rfc7230#section-3.1.1) 小节中有说明,本身不限制长度。但是在实践中,服务器和客户端本身会施加限制*,因此需要根据自己的场景和需求做对应的调整 -所有 URI 都需要遵循 [RFC 3986](http://tools.ietf.org/html/rfc3986) 的要求。 +* 比如 IE8 的 URL 最大长度是 2083 个字符;nginx 的 [`large_client_header_buffers`](http://nginx.org/en/docs/http/ngx_http_core_module.html#large_client_header_buffers) 默认值是 8k ,整个 [request-line](https://tools.ietf.org/html/rfc7230#section-3.1.1) 超过 8k 时就会返回 414 (Request-URI Too Large) +* [Microsoft REST API Guidelines - 7.2. URL length](https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#72-url-length) -**强烈建议 API 部署 SSL 证书**,这样接口传递的数据的安全性才能都得一定的保障。 +**强烈建议 API 部署 SSL 证书**,这样接口传递的数据的安全性才能获得一定的保障。 ## 空字段 @@ -111,17 +115,29 @@ language-script-region-variant-extension-privateuse 客户端请求服务器时,如果对时间有特殊要求(如某段时间每天的统计信息),则可以参考 [IETF 相关草案](http://tools.ietf.org/html/draft-sharhalakis-httptz-05) 增加请求头 `Timezone` 。 ``` -Timezone: 2007-06-12T23:48:22+0800 -// OR -Timezone: 1977-07-30T12:00:11+0200;;Europe/Athens +Timezone: 2016-11-06 23:55:52+08:00;;Asia/Shanghai +``` + +具体格式说明: + +``` +Timezone: RFC3339 约定的时间格式;POSIX 1003.1 约定的时区字符串;tz datebase 里的时区名称 ``` -时区的名称可以参考 [tz datebase](http://www.iana.org/time-zones)([Wikipedia](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)) 。 +客户端最好提供所有字段,如果没有办法提供,则应该使用空字符串 -如果客户端请求时没有指定相应的时区,则服务端默认使用 [UTC](http://zh.wikipedia.org/wiki/%E5%8D%8F%E8%B0%83%E4%B8%96%E7%95%8C%E6%97%B6) 时间返回相应数据。 +如果客户端请求时没有指定相应的时区,则服务端默认使用最后一次已知时区或者 [UTC](http://zh.wikipedia.org/wiki/%E5%8D%8F%E8%B0%83%E4%B8%96%E7%95%8C%E6%97%B6) 时间返回相应数据。 PS 考虑到存在[夏时制](https://en.wikipedia.org/wiki/Daylight_saving_time)这种东西,所以不推荐客户端在请求时使用 Offset 。 +相关资料: + +* [RFC3339](https://tools.ietf.org/html/rfc3339) +* [tz datebase](http://www.iana.org/time-zones) ([Wikipedia](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)) +* POSIX 1003.1 时区字符串的说明文档 + * [GNU 的文档](https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html) + * [IBM 的文章](https://www.ibm.com/developerworks/aix/library/au-aix-posix/) + ### 时间格式 时间格式遵循 [ISO 8601](https://www.iso.org/obp/ui/#iso:std:iso:8601:ed-3:v1:en)([Wikipedia](https://en.wikipedia.org/wiki/ISO_8601)) 建议的格式: @@ -148,9 +164,9 @@ PS 考虑到存在[夏时制](https://en.wikipedia.org/wiki/Daylight_saving_time ## 请求方法 -* 如果请求头中存在 `X-HTTP-Method-Override` 或参数中存在 `_method`(拥有更高权重),且值为 `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `OPTION`, `HEAD` 之一,则视作相应的请求方式进行处理 +* 如果请求头中存在 `X-HTTP-Method-Override` 或参数中存在 `_method`(拥有更高权重),且值为 `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `OPTIONS`, `HEAD` 之一,则视作相应的请求方式进行处理 * `GET`, `DELETE`, `HEAD` 方法,参数风格为标准的 `GET` 风格的参数,如 `url?a=1&b=2` -* `POST`, `PUT`, `PATCH`, `OPTION` 方法 +* `POST`, `PUT`, `PATCH`, `OPTIONS` 方法 * 默认情况下请求实体会被视作标准 json 字符串进行处理,当然,依旧推荐设置头信息的 `Content-Type` 为 `application/json` * 在一些特殊接口中(会在文档中说明),可能允许 `Content-Type` 为 `application/x-www-form-urlencoded` 或者 `multipart/form-data` ,此时请求实体会被视作标准 `POST` 风格的参数进行处理 @@ -195,7 +211,7 @@ PS 考虑到存在[夏时制](https://en.wikipedia.org/wiki/Daylight_saving_time * 301 **Moved Permanently** : 被请求的资源已永久移动到新位置 * 302 **Found** : 请求的资源现在临时从不同的 URI 响应请求 -* 303 **See Other** : 对应当前请求的响应可以在另一个 URI 上被找到,客户端应该使用 `GET` 方法进行请求 +* 303 **See Other** : 对应当前请求的响应可以在另一个 URI 上被找到,客户端应该使用 `GET` 方法进行请求。比如在创建已经被创建的资源时,可以返回 `303` * 307 **Temporary Redirect** : 对应当前请求的响应可以在另一个 URI 上被找到,客户端应该保持原有的请求方法进行请求 ### 条件请求 @@ -207,7 +223,7 @@ PS 考虑到存在[夏时制](https://en.wikipedia.org/wiki/Daylight_saving_time ### 客户端错误 * 400 **Bad Request** : 请求体包含语法错误 -* 401 **Unauthorized** : 需要验证用户身份,如果服务器就算是身份验证后也不允许客户访问资源,应该响应 `403 Forbidden` +* 401 **Unauthorized** : 需要验证用户身份,如果服务器就算是身份验证后也不允许客户访问资源,应该响应 `403 Forbidden` 。如果请求里有 `Authorization` 头,那么必须返回一个 [`WWW-Authenticate`](https://tools.ietf.org/html/rfc7235#section-4.1) 头 * 403 **Forbidden** : 服务器拒绝执行 * 404 **Not Found** : 找不到目标资源 * 405 **Method Not Allowed** : 不允许执行目标方法,响应中应该带有 `Allow` 头,内容为对该资源有效的 HTTP 方法 @@ -235,112 +251,20 @@ PS 考虑到存在[夏时制](https://en.wikipedia.org/wiki/Daylight_saving_time * [维基百科上的《 HTTP 状态码》词条](http://zh.wikipedia.org/wiki/HTTP%E7%8A%B6%E6%80%81%E7%A0%81) * [Do I need to use http redirect code 302 or 307? - Stack Overflow](http://stackoverflow.com/questions/2467664/do-i-need-to-use-http-redirect-code-302-or-307) * [400 vs 422 response to POST of data](http://stackoverflow.com/questions/16133923/400-vs-422-response-to-post-of-data) - -## 错误处理 - -在调用接口的过程中,可能出现下列几种错误情况: - -* 服务器维护中,`503` 状态码 - - ```http - HTTP/1.1 503 Service Unavailable - Retry-After: 3600 - Content-Length: 41 - - {"message": "Service In the maintenance"} - ``` - -* 发送了无法转化的请求体,`400` 状态码 - - ```http - HTTP/1.1 400 Bad Request - Content-Length: 35 - - {"message": "Problems parsing JSON"} - ``` - -* 服务到期(比如付费的增值服务等), `403` 状态码 - - ```http - HTTP/1.1 403 Forbidden - Content-Length: 29 - - {"message": "Service expired"} - ``` - -* 因为某些原因不允许访问(比如被 ban ),`403` 状态码 - - ```http - HTTP/1.1 403 Forbidden - Content-Length: 29 - - {"message": "Account blocked"} - ``` - -* 权限不够,`403` 状态码 - - ```http - HTTP/1.1 403 Forbidden - Content-Length: 31 - - {"message": "Permission denied"} - ``` - -* 需要修改的资源不存在, `404` 状态码 - - ```http - HTTP/1.1 404 Not Found - Content-Length: 32 - - {"message": "Resource not found"} - ``` - -* 缺少了必要的头信息,`428` 状态码 - - ```http - HTTP/1.1 428 Precondition Required - Content-Length: 35 - - {"message": "Header User-Agent is required"} - ``` - -* 发送了非法的资源,`422` 状态码 - - ```http - HTTP/1.1 422 Unprocessable Entity - Content-Length: 149 - - { - "message": "Validation Failed", - "errors": [ - { - "resource": "Issue", - "field": "title", - "code": "missing_field" - } - ] - } - ``` - -所有的 `error` 哈希表都有 `resource`, `field`, `code` 字段,以便于定位错误,`code` 字段则用于表示错误类型: - -* `missing`: 说明某个字段的值代表的资源不存在 -* `invalid`: 某个字段的值非法,接口文档中会提供相应的信息 -* `missing_field`: 缺失某个必须的字段 -* `already_exist`: 发送的资源中的某个字段的值和服务器中已有的某个资源冲突,常见于某些值全局唯一的字段,比如 @ 用的用户名(这个错误我有纠结,因为其实有 409 状态码可以表示,但是在修改某个资源时,很一般显然请求中不止是一种错误,如果是 409 的话,多种错误的场景就不合适了) +* [HTTP Status Codes Decision Diagram – Infographic](https://www.loggly.com/blog/http-status-code-diagram/) +* [HTTP Status Codes](https://httpstatuses.com/) ## 身份验证 部分接口需要通过某种身份验证方式才能请求成功(这些接口**应该**在文档中标注出来),合适的身份验证解决方案目前有两种: -* [HTTP 基本认证](http://zh.wikipedia.org/wiki/HTTP%E5%9F%BA%E6%9C%AC%E8%AE%A4%E8%AF%81),**只有在部署了 SSL 证书的情况下才可以使用,否则用户密码会有暴露的风险,当然,最好不要使用** -* [JSON Web Token](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25) ,支持通过登录接口使用账号密码获取,在请求接口时使用 `Authorization: Bearer #{token}` 头标或者 `token` 参数的值的方式进行验证。 - * [Json Web Tokens: Introduction](http://angular-tips.com/blog/2014/05/json-web-tokens-introduction/) - * [Json Web Tokens: Examples](http://angular-tips.com/blog/2014/05/json-web-tokens-examples/) - * [Cookies vs Tokens. Getting auth right with Angular.JS](https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/) +* [HTTP 基本认证](http://zh.wikipedia.org/wiki/HTTP%E5%9F%BA%E6%9C%AC%E8%AE%A4%E8%AF%81),**最好只在部署了 SSL 证书的情况下才可以使用,否则用户密码会有暴露的风险** * [OAuth 2.0](https://tools.ietf.org/html/rfc6749) * [官网](http://oauth.net/2/) * [理解OAuth 2.0 - 阮一峰](http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html) 以及对[文中 `state` 参数的介绍的修正](http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html#comment-323002) + * [JSON Web Token](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25) ,一种 Token 的生成标准 + * [Json Web Tokens: Introduction](http://angular-tips.com/blog/2014/05/json-web-tokens-introduction/) + * [Json Web Tokens: Examples](http://angular-tips.com/blog/2014/05/json-web-tokens-examples/) ## 超文本驱动和资源发现 @@ -355,36 +279,6 @@ REST 服务的要求之一就是[超文本驱动](http://roy.gbiv.com/untangled/ 目前所知的方案都实现了发现资源的功能,服务端同时需要实现 `OPTIONS` 方法,并在响应中携带 `Allow` 头来告知客户端当前拥有的操作权限。 -## 分页 - -请求某个资源集合时,可以通过指定 `count` 参数来指定每页的资源数量,通过 `page` 参数指定页码,或根据 `last_cursor` 参数指定上一页最后一个资源的标识符。 - -如果没有传递 `count` 参数或者 `count` 参数的值为空,则使用默认值 20 , `count` 参数的最大上限为 100 。 - -如何同时传递了 `last_cursor` 和 `page` 参数,则使用 `page` 。 - -分页的相关信息会包含在 [Link Header](http://tools.ietf.org/html/rfc5988) 和 `X-Total-Count` 中。 - -如果是第一页或者是最后一页时,不会返回 `previous` 和 `next` 的 Link 。 - -```http -HTTP/1.1 200 OK -X-Total-Count: 542 -Link: ; rel="first", - ; rel="last" - ; rel="previous", - ; rel="next", - -[ - ... -] -``` - -相关资料: - -* [RFC 5005 第3节 _Paged Feeds_](http://tools.ietf.org/html/rfc5005#section-3) -* [RFC 5988 6.2.2节 _Initial Registry Contents_](http://tools.ietf.org/html/rfc5988#section-6.2.2) - ## 数据缓存 大部分接口应该在响应头中携带 `Last-Modified`, `ETag`, `Vary`, `Date` 信息,客户端可以在随后请求这些资源的时候,在请求头中使用 `If-Modified-Since`, `If-None-Match` 等请求头来确认资源是否经过修改。 @@ -509,12 +403,19 @@ foo({ * [Httpbis Status Pages](https://tools.ietf.org/wg/httpbis/) * [所有在 IANA 注册的消息头和相关标准的列表](http://www.iana.org/assignments/message-headers/message-headers.xhtml) +* [Standards.REST](https://standards.rest/) 里面收集了不少对 REST API 设计有借鉴意义的标准和规范 -## 更细节的接口设计指南 +## 其他接口设计指南 这里还有一些其他参考资料: -* 推荐参考文档 [HTTP API Design Guide](https://github.com/interagent/http-api-design/) 来设计 REST 风格的 API ,只有以下两点我个人并不建议参考: +* [Microsoft REST API Guidelines](https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md) ,很多设计都很有意思,比如: + * [7.10.2. Error condition responses](https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#7102-error-condition-responses) + * [9.8. Pagination](https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#98-pagination) + * [10. Delta queries](https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#10-delta-queries) + * [13. Long running operations](https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#13-long-running-operations) +* [GitHub Developer - REST API v3](https://developer.github.com/v3/) +* [HTTP API Design Guide](https://github.com/interagent/http-api-design/) ,有以下两点我个人并不建议参考: * [Use consistent path formats](https://github.com/interagent/http-api-design/#use-consistent-path-formats) 还是不建议将动作写在 URL 中,像文档中的情况,可以将这个行为抽象成一个事务资源 `POST /runs/:run_id/stop-logs` 或者 `POST /runs/:run_id/stoppers` 来解决 * [Paginate with Ranges](https://github.com/interagent/http-api-design/#paginate-with-ranges) @@ -522,6 +423,7 @@ foo({ * [Best Practices for Designing a Pragmatic RESTful API](http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api) * [Thoughts on RESTful API Design](http://restful-api-design.readthedocs.org/en/latest/) * [The RESTful CookBook](http://restcookbook.com/) +* [Google API 设计指南](https://docs.cloud.google.com/apis/design) - 这是一份从 2014 年起开始在 Google 内部使用的的 API 设计指南,我没有看完,不过觉得如果问题的时候过去看看找找灵感可能也不错。“本指南是一份活文档,随着时间的推移,我们会采纳和批准新的风格和设计模式,为本指南增加相关内容。本着这种精神,我们会不断完善本指南,并为 API 设计的艺术和技巧提供充足的空间。” [iso3166-1]: javascript:; [iso3166-1_wiki]: http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 diff --git a/SUPPLEMENT.md b/SUPPLEMENT.md index 78efb36..ca34769 100644 --- a/SUPPLEMENT.md +++ b/SUPPLEMENT.md @@ -6,9 +6,12 @@ * [扩充巴科斯范式](#user-content-扩充巴科斯范式-abnf) * [User-Agent](#user-content-user-agent) +* [WWW-Authenticate 头](#user-content-www-authenticate-头) * [两步验证](#user-content-两步验证) * [同时操作多个资源](#user-content-同时操作多个资源) * [超文本驱动](#user-content-超文本驱动) +* [错误处理](#user-content-错误处理) +* [分页](#user-content-分页) ## 扩充巴科斯范式 (ABNF) @@ -53,10 +56,16 @@ obs-fold = CRLF 1*( SP / HTAB ) Android 的网络类型获取可以参考文档:[http://developer.android.com/reference/android/telephony/TelephonyManager.html](http://developer.android.com/reference/android/telephony/TelephonyManager.html) +## WWW-Authenticate 头 + +如果是自定义的身份验证方式,比如要求请求时带上请求头 `Authentication: Token `,那么一般在 token 验证失败返回 `401` 的 `WWW-Authenticate` 头可以是 `WWW-Authenticate: Token` ,当然也可以带上任意其他自定义信息。客户端在发现自己无法识别的信息时应该略过。 + ## 两步验证 如果只是打算简单实现,建议使用 [TOTP](http://tools.ietf.org/html/rfc6238)([Wikipedia](http://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm)) 协议,可以兼容 [Google Authenticator](https://code.google.com/p/google-authenticator/) 。 +关于如何在 API 中实现对两步验证的支持,可以参考 [GitHub 的文档](https://developer.github.com/v3/auth/#working-with-two-factor-authentication)。 + 相关资料: * 这里是一份 TOTP 协议中密码生成算法的简单说明:[http://jacob.jkrall.net/totp/](http://jacob.jkrall.net/totp/) @@ -146,15 +155,17 @@ HTTP/1.1 204 No Content ## 超文本驱动 -想法受启发于 [JSON API 方案](http://jsonapi.org/),很多做法基本照搬,主要是把 `links` 相关内容放到了请求头里。 +想法受启发于 [JSON API 方案](http://jsonapi.org/),做法基本照搬,主要是把 `links` 相关内容放到了响应头里。 -想法目前还不成熟,并不建议投入使用。 +可以添加 `schema` 参数链接到目标数据的结构描述文档,比如 [JSON Schema](http://json-schema.org/) 、 [Schema.org](http://schema.org/) 等。 + +想法目前还不成熟,不建议投入使用。 ```http HTTP/1.1 200 OK -Link: ; rel="res:author"; allow="collection,get", - ; rel="res:comments"; allow="collection,create,get,delete", - ; rel="res:order"; allow="get,put" +Link: ; rel="url-template:author"; allow="COLLECTION,GET"; schema="...", + ; rel="url-template:comments"; allow="COLLECTION,CREATE,GET,DELETE"; schema="...", + ; rel="url-template:order"; allow="GET,PUT"; schema="..." [{ "id": "1", @@ -163,3 +174,134 @@ Link: ; rel="res:author"; allow=" "comments": [ "5", "12", "17", "20" ] }] ``` + +## 错误处理 + +在调用接口的过程中,可能出现下列几种错误情况: + +* 服务器维护中,`503` 状态码 + + ```http + HTTP/1.1 503 Service Unavailable + Retry-After: 3600 + Content-Length: 41 + + {"message": "Service In the maintenance"} + ``` + +* 发送了无法转化的请求体,`400` 状态码 + + ```http + HTTP/1.1 400 Bad Request + Content-Length: 35 + + {"message": "Problems parsing JSON"} + ``` + +* 服务到期(比如付费的增值服务等), `403` 状态码 + + ```http + HTTP/1.1 403 Forbidden + Content-Length: 29 + + {"message": "Service expired"} + ``` + +* 因为某些原因不允许访问(比如被 ban ),`403` 状态码 + + ```http + HTTP/1.1 403 Forbidden + Content-Length: 29 + + {"message": "Account blocked"} + ``` + +* 权限不够,`403` 状态码 + + ```http + HTTP/1.1 403 Forbidden + Content-Length: 31 + + {"message": "Permission denied"} + ``` + +* 需要修改的资源不存在, `404` 状态码 + + ```http + HTTP/1.1 404 Not Found + Content-Length: 32 + + {"message": "Resource not found"} + ``` + +* 缺少了必要的头信息,`428` 状态码 + + ```http + HTTP/1.1 428 Precondition Required + Content-Length: 35 + + {"message": "Header User-Agent is required"} + ``` + +* 发送了非法的资源,`422` 状态码 + + ```http + HTTP/1.1 422 Unprocessable Entity + Content-Length: 149 + + { + "message": "Validation Failed", + "errors": [ + { + "resource": "Issue", + "field": "title", + "code": "required" + } + ] + } + ``` + +所有的 `error` 哈希表都有 `resource`, `field`, `code` 字段,以便于定位错误,`code` 字段则用于表示错误类型: + +* `invalid`: 某个字段的值非法,接口文档中会提供相应的信息 +* `required`: 缺失某个必须的字段 +* `not_exist`: 说明某个字段的值代表的资源不存在 +* `already_exist`: 发送的资源中的某个字段的值和服务器中已有的某个资源冲突,常见于某些值全局唯一的字段,比如 @ 用的用户名(这个错误我有纠结,因为其实有 409 状态码可以表示,但是在修改某个资源时,很一般显然请求中不止是一种错误,如果是 409 的话,多种错误的场景就不合适了) + +其他参考: + +* [Microsoft REST API Guidelines - 7.10.2. Error condition responses](https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#7102-error-condition-responses) +* [GitHub Developer - Client errors](https://developer.github.com/v3/#client-errors) + +## 分页 + +请求某个资源集合时,可以通过指定 `count` 参数来指定每页的资源数量,通过 `page` 参数指定页码,或根据需求使用 `last_cursor` 参数指定上一页最后一个资源的标识符替代 `page` 参数。 + +如果没有传递 `count` 参数或者 `count` 参数的值为空,则使用默认值,建议在设计时设置一个最大值。 + +分页的相关信息可以包含在 [Link Header](http://tools.ietf.org/html/rfc5988) 和 `X-Pagination-Info` 中( HTTP 头的语法格式可以参考 [ABNF List Extension: #rule](https://tools.ietf.org/html/rfc7230#section-7) )。 + +如果是第一页或者是最后一页时,不返回 `previous` 和 `next` 的 Link 。 + +```http +HTTP/1.1 200 OK +X-Pagination-Info: count="542" +Link: ; rel="first", + ; rel="last", + ; rel="previous", + ; rel="next", + ; rel="url-template:pagination" + +[ + ... +] +``` + +相关资料: + +* [RFC 5005 第3节 _Paged Feeds_](http://tools.ietf.org/html/rfc5005#section-3) +* [RFC 5988 6.2.2节 _Initial Registry Contents_](http://tools.ietf.org/html/rfc5988#section-6.2.2) + +其他参考: + +* [Microsoft REST API Guidelines - 9.8. Pagination](https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#98-pagination)