序
本文主要简述API设计的要点。整体会偏向HTTP的接口,部分同样适合非HTTP的接口。
API版本管理
多版本兼容(控制维护的版本数,不宜过多
)
url中指定版本信息
HTTP GET: https://haveibeenpwned.com/api/v2/breachedaccount/foorequest header
HTTP GET:
https://haveibeenpwned.com/api/breachedaccount/foo
api-version: 2
spring中直接@RequestMapping(headers="api-version=2")即可以自动分发
content-type accept
HTTP GET:
https://haveibeenpwned.com/api/breachedaccount/foo
Accept: application/vnd.haveibeenpwned.v2+json
content-type accept(版本单独字段)
HTTP GET:
https://haveibeenpwned.com/api/breachedaccount/foo
Accept: application/vnd.haveibeenpwned+json; version=2.0
如何下架旧版本(对过期版本的处理
)
对既有版本的访问进行监控
对要下架的版本随机返回错误
API安全认证(两方认证vs三方认证
)
两方认证,只涉及api服务提供方和使用方;
三方认证,多了个用户,通常是采用OAuth2来进行授权认证。
下面的这些仅仅涉及两方认证(双方认证的 api 通常的认证方式是使用一组 id 和密钥,用 id 来标记应用,用密钥来对请求做签名
):
开启https(
web api的话
)Http Basic Authentication(
一般内网使用配合https
)
在 Http header 中添加键值对 Authorization: Basic xxx (xxx 是 username:passowrd base64 值)。黑白名单(
内网白,外网黑
)JWT (JSON Web Token)(
适合移动端
)
JWT认证协议的格式为Header.Payload.Signature。比如xxxxx.yyyyy.zzzzz。签名内容是有Header+Payload+Secret通过HMAC SHA256算法加密而成。
具体参考Introduction to JSON Web Tokens
API限流措施
-
实例
google cloud api rate limit
github rate limit
instagram limits
微博接口访问频次权限
限流方案
简单的可以用nginx的ip限流,复杂的可以借用redis的计数器来实现各种规则的限流。http的X-RateLimit header
HTTP/1.1 403 Forbidden
Date: Tue, 20 Aug 2013 14:50:41 GMT
Status: 403 Forbidden
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1377013266
{
"message": "API rate limit exceeded. \
See http://developer.github.com/v3/ \
#rate-limiting for details."
}
API的文档化
业界的两个方案,一个是Swagger使用Swagger快速打造REST API文档,一个是最新的spring restdocs
API的接口设计
-
错误码处理
建议充分利用http的错误码,然后再细分,不建议自己再重新自定义一套。-
请求成功
200 OK : 请求执行成功并返回相应数据,如
GET
成功201 Created : 对象创建成功并返回相应资源数据,如
POST
成功;创建完成后响应头中应该携带头标Location
,指向新建资源的地址202 Accepted : 接受请求,但无法立即完成创建行为,比如其中涉及到一个需要花费若干小时才能完成的任务。返回的实体中应该包含当前状态的信息,以及指向处理状态监视器或状态预测的指针,以便客户端能够获取最新状态。
204 No Content : 请求执行成功,不返回相应资源数据,如
PATCH
,DELETE
成功
-
重定向
重定向的新地址都需要在响应头Location
中返回301 Moved Permanently : 被请求的资源已永久移动到新位置
302 Found : 请求的资源现在临时从不同的 URI 响应请求
303 See Other : 对应当前请求的响应可以在另一个 URI 上被找到,客户端应该使用
GET
方法进行请求307 Temporary Redirect : 对应当前请求的响应可以在另一个 URI 上被找到,客户端应该保持原有的请求方法进行请求
-
条件请求
304 Not Modified : 资源自从上次请求后没有再次发生变化,主要使用场景在于实现数据缓存
409 Conflict : 请求操作和资源的当前状态存在冲突。主要使用场景在于实现并发控制
412 Precondition Failed : 服务器在验证在请求的头字段中给出先决条件时,没能满足其中的一个或多个。主要使用场景在于实现并发控制
-
客户端错误
400 Bad Request : 请求体包含语法错误
401 Unauthorized : 需要验证用户身份,如果服务器就算是身份验证后也不允许客户访问资源,应该响应
403 Forbidden
403 Forbidden : 服务器拒绝执行
404 Not Found : 找不到目标资源
405 Method Not Allowed : 不允许执行目标方法,响应中应该带有
Allow
头,内容为对该资源有效的 HTTP 方法406 Not Acceptable : 服务器不支持客户端请求的内容格式,但响应里会包含服务端能够给出的格式的数据,并在
Content-Type
中声明格式名称410 Gone : 被请求的资源已被删除,只有在确定了这种情况是永久性的时候才可以使用,否则建议使用
404 Not Found
413 Payload Too Large :
POST
或者PUT
请求的消息实体过大415 Unsupported Media Type : 服务器不支持请求中提交的数据的格式
422 Unprocessable Entity : 请求格式正确,但是由于含有语义错误,无法响应
428 Precondition Required : 要求先决条件,如果想要请求能成功必须满足一些预设的条件
-
服务端错误
500 Internal Server Error : 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。
501 Not Implemented : 服务器不支持当前请求所需要的某个功能。
501
与405
的区别是:405
是表示服务端不允许客户端这么做,501
是表示客户端或许可以这么做,但服务端还没有实现这个功能502 Bad Gateway : 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
503 Service Unavailable : 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。如果能够预计延迟时间,那么响应中可以包含一个
Retry-After
头用以标明这个延迟时间(内容可以为数字,单位为秒;或者是一个 HTTP 协议指定的时间格式)。如果没有给出这个Retry-After
信息,那么客户端应当以处理 500 响应的方式处理它。
错误码带描述
-
503 Service Unavailable
{
"error_human": "The Database is currently unavailable.",
"error_code": "database_unavailable" (使用文字描述比起使用数字code会使开发者更容易懂)
}
分页
GET /employees?left=true&fields=name,joinDate&limit=10&offset=0&sortby=name&order=asc
{
"data": {
"total": 150,
"employees": [
{
"name": "tom",
"joinDate": "2015-10-01"
},
{
"name": "jack",
"joinDate": "2015-10-02"
}
]
}
}
或者采用page的方式同时加入links提示
?page=1&pre_page=10
{
"data": {
"total": 150,
"links": [
{
"rel": "next",
"href": "?page=2&per_page=10"
},
{
"rel": "last",
"href": "?page=11&per_page=10"
}
],
"employees": [
{
"name": "tom",
"joinDate": "2015-10-01"
},
{
"name": "jack",
"joinDate": "2015-10-02"
}
]
}
}
或者
?limit=10: Reduce the number of results returned to the Consumer (for Pagination)
?offset=10: Send sets of information to the Consumer (for Pagination)
?limit=25&offset=50
排序
?sortby=name&order=asc: Sort the results based on the specified attribute (ORDER BY name ASC)
查询字段选择
GET http://api.example.org/user/12?fields=id,name,email (SELECT id, name, email FROM user WHERE user.id = 12)
查询过滤
?q=fluffy+fur(搜索)
?animal_type_id=1: Filter records which match the following condition (WHERE animal_type_id = 1)
-
其他
HATEOAS
HATEOAS是Hypermedia as the Engine of Application State的缩写形式,中文意思为:超媒体应用状态引擎。它的核心思想是使用超媒体表达应用状态,与hypertext-driven思想是一致的。在此之前,我们大多数的程序业务控制在前台完成。例如:我们会在前台做注册流程,我们在前台判定下一步应该做什么,可以做什么。当使用HATEOAS时,这些状态流程控制都在应用程序的后台完成。我们使用超媒体来表达前台做完某一步骤之后可以做哪些? 这样一来,前台的任务就变得相当简单了,前台需要处理的是理解状态表述,数据收集和结果显示。-
url组成
网络协议(HTTP, HTTPS)
服务器地址
版本
接口名称(
用-代替_
)?参数列表(
之后返回接口instagram是用_代替驼峰的
)
-
url命名限制
不使用大写字母
使用中线-代替下划线_,比如instagram的一个接口,https://api.instagram.com/v1/users/{user-id}/followed-by?access_token=ACCESS-TOKEN
参数列表应该被encode过
聚合资源必须通过父级资源操作
示例: Profile是User的聚合资源,User有一个唯一且私有的Profile资源,只能通过User操作Profile。/users/123456/profiles组合资源要避免资源路径嵌套
示例: 一个系统里面包含多个 applications,一个 application 又包含多个 users。那获取 user 资源的路径应该是怎样的?
GET http://~/$version/systems/:systemId/applications/:applicationId/users/:userId
改为
GET http://~/$version/systems/:systemId
GET http://~/$version/applications/:applicationId
GET http://~/$version/users/:userId/
参考
HTTP API 设计指南
Web API 版本控制的几种方式
How are REST APIs versioned?
RESTful API版本控制策略
api如何设计才能支撑多个不同版本的app共存
GitHub API: Moving On
深入理解 RESTful Api 架构
RESTful Api 身份认证中的安全性设计探讨
API 双方认证探讨
JSON Web Token - 在Web应用间安全地传递信息
八幅漫画理解使用JSON Web Token设计单点登录系统
Json Web Token身份认证(
推荐
)Token Authentication for Java Applications(
推荐
)Spring Boot JWT Sample Quickstart
How To Rate-Limit An API
HTTP 接口设计指北(
推荐
)Google JSON风格指南 (
强烈推荐
)RESTful最佳实践(
重点推荐
)