Home

Awesome

HTTP 接口设计指北

文档主要目的是为大家在设计接口时提供建议,给大家参考 HTTP 或者其他协议/指南已经设计过的内容

只是建议,不是必须遵从的要求

大家有什么问题想法或者建议欢迎 创建 Issue 或者 提交 Pull Request

目录

HTTP 协议

HTTP/1.1

2014 年 6 月的时候 IETF 已经正式的废弃了 RFC 2616 ,将它拆分为六个单独的协议说明,并重点对原来语义模糊的部分进行了解释:

相关资料:

HTTP/2

HTTP 协议的 2.0 版本还没有正式发布,但目前已经基本稳定下来了。

2.0 版本的设计目标是尽量在使用层面上保持与 1.1 版本的兼容,所以,虽然数据交换的格式发生了变化,但语义基本全部被保留下来了

因此,作为使用者而言,我们并不需要为了支持 2.0 而大幅修改代码。

URL

URL 的设计都需要遵守 RFC 3986 的的规范。

URL 的长度,在 HTTP/1.1: Message Syntax and Routing(RFC 7230) 的 3.1.1 小节中有说明,本身不限制长度。但是在实践中,服务器和客户端本身会施加限制*,因此需要根据自己的场景和需求做对应的调整

强烈建议 API 部署 SSL 证书,这样接口传递的数据的安全性才能获得一定的保障。

空字段

接口遵循“输入宽容,输出严格”原则,输出的数据结构中空字段的值一律为 null

国际化

语言标签

RFC 5646 (BCP 47) 规定的语言标签的格式如下:

language-script-region-variant-extension-privateuse
  1. language:这部分使用的是 ISO 639-1, ISO 639-2, ISO 639-3, ISO 639-5 中定义的语言代码,必填
    • 这个部分由 primary-extlang 两个部分构成
    • primary 部分使用 ISO 639-1, ISO 639-2, ISO 639-3, ISO 639-5 中定义的语言代码,优先使用 ISO 639-1 中定义的条目,比如汉语 zh
    • extlang 部分是在某些历史性的兼容性的原因,在需要非常细致地区别 primary 语言的时候使用,使用 ISO 639-3 中定义的三个字母的代码,比如普通话 cmn
    • 虽然 language 可以只写 extlang 省略 primary 部分,但出于兼容性的考虑,还是建议加上 primary 部分
  2. script: 这部分使用的是 ISO 15924 (Wikipedia) 中定义的语言代码,比如简体汉字是 zh-Hans ,繁体汉字是 zh-Hant
  3. region: 这部分使用的是 ISO 3166-1 (Wikipedia) 中定义的地理区域代码,比如 zh-Hans-CN 就是中国大陆使用的简体中文。
  4. variant: 用来表示 extlang 的定义里没有包含的方言,具体的使用方法可以参考 RFC 5646
  5. extension: 用来为自己的应用做一些语言上的额外的扩展,具体的使用方法可以参考 RFC 5646
  6. privateuse: 用来表示私有协议中约定的一些语言上的区别,具体的使用方法可以参考 RFC 5646

其中只有 language 部分是必须的,其他部分都是可选的;不过为了便于编写程序,建议设计接口时约定语言标签的结构,比如统一使用 language-script-region 的形式( zh-Hans-CN, zh-Hant-HK 等等)。

语言标签是大小写不敏感的,但按照惯例,建议 script 部分首字母大写, region 部分全部大写,其余部分全部小写。

有一点需要注意,任何合法的标签都必须经过 IANA 的认证,已通过认证的标签可以在这个网页查到。此外,网上还有一个非官方的标签搜索引擎

相关资料:

时区

客户端请求服务器时,如果对时间有特殊要求(如某段时间每天的统计信息),则可以参考 IETF 相关草案 增加请求头 Timezone

Timezone: 2016-11-06 23:55:52+08:00;;Asia/Shanghai

具体格式说明:

Timezone: RFC3339 约定的时间格式;POSIX 1003.1 约定的时区字符串;tz datebase 里的时区名称

客户端最好提供所有字段,如果没有办法提供,则应该使用空字符串

如果客户端请求时没有指定相应的时区,则服务端默认使用最后一次已知时区或者 UTC 时间返回相应数据。

PS 考虑到存在夏时制这种东西,所以不推荐客户端在请求时使用 Offset 。

相关资料:

时间格式

时间格式遵循 ISO 8601(Wikipedia) 建议的格式:

相关资料:

货币名称

货币名称可以参考 ISO 4217(Wikipedia) 中的约定,标准为货币名称规定了三个字母的货币代码,其中的前两个字母是 ISO 3166-1(Wikipedia) 中定义的双字母国家代码,第三个字母通常是货币的首字母。在货币上使用这些代码消除了货币名称(比如 dollar )或符号(比如 $ )的歧义。

相关资料:

请求方法

关于方法语义的说明:

相关资料:

状态码

请求成功

重定向

重定向的新地址都需要在响应头 Location 中返回

条件请求

客户端错误

服务端错误

501405 的区别是:405 是表示服务端不允许客户端这么做,501 是表示客户端或许可以这么做,但服务端还没有实现这个功能

相关资料:

身份验证

部分接口需要通过某种身份验证方式才能请求成功(这些接口应该在文档中标注出来),合适的身份验证解决方案目前有两种:

超文本驱动和资源发现

REST 服务的要求之一就是超文本驱动,客户端不再需要将某些接口的 URI 硬编码在代码中,唯一需要存储的只是 API 的 HOST 地址,能够非常有效的降低客户端与服务端之间的耦合,服务端对 URI 的任何改动都不会影响到客户端的稳定。

目前有几种方案试图实现这个效果:

目前所知的方案都实现了发现资源的功能,服务端同时需要实现 OPTIONS 方法,并在响应中携带 Allow 头来告知客户端当前拥有的操作权限。

数据缓存

大部分接口应该在响应头中携带 Last-Modified, ETag, Vary, Date 信息,客户端可以在随后请求这些资源的时候,在请求头中使用 If-Modified-Since, If-None-Match 等请求头来确认资源是否经过修改。

如果资源没有进行过修改,那么就可以响应 304 Not Modified 并且不在响应实体中返回任何内容。

$ curl -i http://api.example.com/#{RESOURCE_URI}
HTTP/1.1 200 OK
Cache-Control: public, max-age=60
Date: Thu, 05 Jul 2012 15:31:30 GMT
Vary: Accept, Authorization
ETag: "644b5b0155e6404a9cc4bd9d8b1ae730"
Last-Modified: Thu, 05 Jul 2012 15:31:30 GMT

Content
$ curl -i http://api.example.com/#{RESOURCE_URI} -H "If-Modified-Since: Thu, 05 Jul 2012 15:31:30 GMT"
HTTP/1.1 304 Not Modified
Cache-Control: public, max-age=60
Date: Thu, 05 Jul 2012 15:31:45 GMT
Vary: Accept, Authorization
Last-Modified: Thu, 05 Jul 2012 15:31:30 GMT
$ curl -i http://api.example.com/#{RESOURCE_URI} -H 'If-None-Match: "644b5b0155e6404a9cc4bd9d8b1ae730"'
HTTP/1.1 304 Not Modified
Cache-Control: public, max-age=60
Date: Thu, 05 Jul 2012 15:31:55 GMT
Vary: Accept, Authorization
ETag: "644b5b0155e6404a9cc4bd9d8b1ae730"
Last-Modified: Thu, 05 Jul 2012 15:31:30 GMT

相关资料:

并发控制

不严谨的实现,或者缺少并发控制的 PUTPATCH 请求可能导致 “更新丢失”。这个时候可以使用 Last-Modified 和/或 ETag 头来实现条件请求,支持乐观并发控制。

下文只考虑使用 PUTPATCH 方法更新资源的情况。

相关资料:

跨域

CORS

接口支持“跨域资源共享”(Cross Origin Resource Sharing, CORS)这里这里这份中文资料有一些指导性的资料。

简单示例:

$ curl -i https://api.example.com -H "Origin: http://example.com"
HTTP/1.1 302 Found
$ curl -i https://api.example.com -H "Origin: http://example.com"
HTTP/1.1 302 Found
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag, Link, X-Total-Count
Access-Control-Allow-Credentials: true

预检请求的响应示例:

$ curl -i https://api.example.com -H "Origin: http://example.com" -X OPTIONS
HTTP/1.1 302 Found
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-Requested-With
Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE
Access-Control-Expose-Headers: ETag, Link, X-Total-Count
Access-Control-Max-Age: 86400
Access-Control-Allow-Credentials: true

JSON-P

如果在任何 GET 请求中带有参数 callback ,且值为非空字符串,那么接口将返回如下格式的数据

$ curl http://api.example.com/#{RESOURCE_URI}?callback=foo
foo({
  "meta": {
    "status": 200,
    "X-Total-Count": 542,
    "Link": [
      {"href": "http://api.example.com/#{RESOURCE_URI}?cursor=0&count=100", "rel": "first"},
      {"href": "http://api.example.com/#{RESOURCE_URI}?cursor=90&count=100", "rel": "prev"},
      {"href": "http://api.example.com/#{RESOURCE_URI}?cursor=120&count=100", "rel": "next"},
      {"href": "http://api.example.com/#{RESOURCE_URI}?cursor=200&count=100", "rel": "last"}
    ]
  },
  "data": // data
})

其他资料

其他接口设计指南

这里还有一些其他参考资料: