GET+POST API
基于GET和POST的接口设计
| 动作 | 前缀 | 备注 |
|---|---|---|
| 获取 | get | get{XXX} |
| 获取 | get | get{XXX}List |
| 新增 | add | add{XXX} |
| 修改 | update | update{XXX} |
| 保存 | save | save{XXX} |
| 删除 | delete | delete{XXX} |
| 上传 | upload | upload{XXX} |
| 发送 | send | send{XXX} |
公共参数
APP 端请求
| 参数 | 说明 | 备注 |
|---|---|---|
| network | 网络 | WIFI、4G |
| operator | 运营商 | 中国联通/移动 |
| platform | 平台 | iOS、Android |
| system | 系统 | ios 13.3、android 9 |
| device | 设备型号 | iPhone XR、小米9 |
| udid | 设备唯一标示 | |
| apiVersion | API 版本号 | v1.1、v1.2 |
WEB 端请求
| 参数 | 说明 | 备注 |
|---|---|---|
| appKey | 授权Key | 字符串 |
调用方需向服务方申请 appKey(请求时使用) 和 secretKey(加密时使用)。
RESTful API
REST,即 Representational State Transfer,表现形式状态转换。REST 是一种软件架构风格,用于指导 Web 应用程序接口(Endpoint)的设计和开发。
Resource:每一个 URL 代表一个确定的资源。一个资源可以是一段文字、一张图片、一段音频、一个服务。
Representation:一个资源可以有多种表现形式,比如一篇文章,可以使用Markdown、PDF、HTML的形式呈现出来。客户端和服务器之间,传递该资源的一种表现形式。
State Transfer:客户端通过 HTTP 动词(GET、POST、DELETE 等)与服务器交互,对服务器中的该资源进行操作使得资源状态发生变化,实现“资源的表现形式的状态转换”。
以资源为中心的 URL 设计
基本设计原则
由HTTP方法体现动作,URL体现资源,而不是在URL中体现动作和资源
bad: GET /articles/get/1 good: GET /articles/1使用复数名词集合
bad: GET /article/1 good: GET /articles/1 good: GET /articles嵌套体现资源关系
good: GET /articles/1/comments/2嵌套可以很好体现资源间的关系,但应当避免超过 3 层嵌套,三层以上可能反而导致可读性降低,有过度嵌套之嫌。
基础接口设计规范
| 业务资源操作分类 | HTTP动词 | Endpoint | 描述 |
|---|---|---|---|
| 查询 | GET | /resources/{recourceId} | 查询单个资源(get) |
| 查询 | GET | /resources?q=查询关键字&age=0,10&name=Jack&limit=10&offset=0 | 查询多个资源(list) |
| 创建 | POST | /resources | 创建单个资源(create) |
| 替换(全部更新) | PUT | /resources/{recourceId} | 全部更新(替换)单个资源(update) |
| 补丁(局部更新) | PATCH | /resources/{resourceId} | 部分更新(补丁)单个资源(update) |
| 删除 | DELETE | /resources/{recourceId} | 删除单个资源(delete) |
| 业务资源操作分类 | HTTP动词 | Endpoint | 描述 |
|---|---|---|---|
| 查询 | GET | /resources/{recourceId}/subResources/{subResourceId} | 查询单个子资源(get) |
| 查询 | GET | /resources/{resourceId}/subResources?q=查询关键字&age=0,10&name=Jack&limit=10&offset=0 | 查询多个子资源(list) |
| 创建 | POST | /resources/{resourceId}/subResources | 创建单个子资源(create) |
| 替换(全部更新) | PUT | /resources/{recourceId}/subResources/{subResourceId} | 全部更新(替换)单个子资源(update) |
| 补丁(局部更新) | PATCH | /resources/{resourceId}/subResources/{subResourceId} | 部分更新(补丁)单个子资源(update) |
| 删除 | DELETE | /resources/{recourceId}/subResources/{subResourceId} | 删除单个子资源(delete) |
参数解释:
q:查询关键字
limit:此次查询多少条数据。
offset:从 0 开始的偏移量。
age=0,10:表示查询 age 为[0, 10)区间的数据,,区分起始,同样的还可应用于时间、日期等类型的数据。
age=,10;20,30;40:表示查询age为 [0, 10)、[20,30) 以及大于40的数据,,前面表示开始值,若不提供,表示所有小于,右侧的值的值,,右侧表示结束值,若没有,则表示所有大于,左侧的值,;表示多个值的区分。
其他:
- 更新和创建操作应该返回最新的资源,来通知用户资源的情况。
- 根据RFC3986定义,URL是大小写敏感的。所以为了避免歧义,尽量使用小写字母。
基础接口设计示例
一级资源
| 业务资源操作分类 | HTTP动词 | Endpoint | 描述 |
|---|---|---|---|
| 查询 | GET | /zoos?name=gardon | 获取 zoo 列表 |
| 查询 | GET | /zoos/{zoo_id} | 根据 animal_id 获取一条zoo数据 |
| 创建 | POST | /zoos | 创建一条 zoo 数据 |
| 替换(全部更新) | PUT | /zoos/{zoo_id} | 根据 animal_id 更新一条 zoo 数据 |
| 补丁(局部更新) | PATCH | /zoos/{zoo_id} | 根据 animal_id 更新一条 zoo 数据 |
| 删除 | DELETE | /zoos/{zoo_id} | 根据 animal_id 删除一条 zoo 数据 |
二级资源
| 业务资源操作分类 | HTTP动词 | Endpoint | 描述 |
|---|---|---|---|
| 查询 | GET | /zoos/{zoo_id}/animals | 获取 zoo_id 的 animal 列表 |
| 查询 | GET | /zoos/{zoo_id}/animals/{animal_id} | 根据 id 获取一条zoo_id的animal数据 |
| 创建 | POST | /zoos/{zoo_id}/animals | 创建一条zoo_id的animal数据 |
| 替换(全部更新) | PUT | /zoos/{zoo_id}/animals/{animal_id} | 根据animal_id更新一条zoo_id的animal数据 |
| 补丁(局部更新) | PATCH | /zoos/{zoo_id}/animals/{animal_id} | 根据animal_id更新一条zoo_id的animal数据 |
| 删除 | DELETE | /zoos/{zoo_id}/animals/{animal_id} | 根据animal_id删除一条zoo_id的animal数据 |
不符合基础接口设计的情况如何处理
REST 本身是一种软件架构风格,不同技术团队对 REST 的应用(或者说对 REST 的落地规范)不尽相同,但有两点是确定的,一是尽可能保证 CRUD 风格,二是保证团队内对 REST 的使用规范一致。
四类资源
整体上对 REST 的资源分为四类,前三类在基础接口设计有所体现,第四类可用于特殊场景:
- document:文档资源,是集合资源中的单个资源,通常对应到数据库的一行记录,资源标记
/resources/{resourceId}/subResources/{subResourceId}唯一确认一个文档资源resource。 - collection:集合资源,是仓库资源中的一类资源,通常对应到数据库的一张表中所有数据,资源标记
/resources/{resourceId}/subResources唯一确认一个集合资源sub-resources store:仓库资源,包含若干集合资源,资源标记/resources/{resourceId}/subResources中的resources即为仓库资源,只要包含集合资源即可称为仓库资源,仓库资源本身也是一类集合资源controller:控制器资源,通常表示一种行为或操作,例如发布帖子的 URL/posts/{id}/publish中publish是一种controller资源。
| 业务资源操作分类 | HTTP动词 | Endpoint | 描述 |
|---|---|---|---|
| 行为或操作 | POST | /resources/{recourceId}/controller | 比如通过审核、激活等对资源的操作 |
document、collection、store三级构成资源的层次表达,controller用于不太直观的特殊资源。
controller 资源示例
邮件重发
POST /email/1/resend
GitHub star
添加star POST https://github.com/xxx/yyy/star
取消star POST https://github.com/xxx/yyy/unstar
大型查询
B端表单复杂查询会导致GET请求URL参数拼接过长,基于临时资源的思路转换请求,创建resources的临时子资源filter然后返回结果
GET /resources?k1=v1&k2=v2&k3=v3&k4=v4&k5=v5&k6=v6&k7=v7&k8=v8&k9=v9&k10=v10&k11=v11&k12=v12
POST /resources/filter
批量操作
将batch视为controller资源
DELETE /resources/batch
整体设计示例
风格一:在基础接口设计原则基础上,批量操作使用请求体,定义 filter 应用于复杂查询场景
把基础和拓展的分开
GET /articles/{articleId}
根据articleId查询一篇文章
方法名,getArticlesOne
权限名,get:articles:one
禁止设计该接口(应用场景受限且本方案有替代)
GET /articles?name=MongoDB&star=666,
根据复杂条件查询文章列表
方法名,getArticleBatch
权限名,get:articles:batch
POST /articles
创建一篇或多篇文章
方法名,postArticlesOneOrBatch
权限名,post:articles:one_or_batch
PUT /articles/{articleId}
根据articleId全部更新一篇文章
方法名,putArticlesOne
权限名,put:articles:one
PUT /articles
全部更新多篇文章
方法名,putArticlesBatch
权限名,put:articles:batch
PATCH /articles/{articleId}
根据articleId局部更新一篇文章
方法名,patchArticlesOne
权限名,patch:articles:one
PATCH /articles
局部更新多篇文章
方法名,patchArticlesBatch
权限名,patch:articles:batch
DELETE /articles/{articleId}
根据articleId删除一篇文章
方法名,deleteArticlesOne
权限名,delete:articles:one
DELETE /articles
根据articleId删除多篇文章
方法名,deleteArticlesBatch
权限名,delete:articles:batch
POST /articles/filter
创建filter临时资源,返回过滤结果,针对复杂条件查询场景
方法名,postArticlesFilter
权限名,post:articles:filter
风格二:在基础接口设计原则基础上,定义 batch 资源用于批量操作场景,定义 filter 资源用于复杂条件过滤场景
GET /articles/{articleId}
根据articleId查询一篇文章
方法名,getArticlesOne
权限名,get:articles:one
禁止设计该接口(应用场景受限且本方案有替代)
GET /articles?name=MongoDB&star=666,
根据复杂条件查询文章列表
方法名,getArticlesBatch
权限名,get:articles:batch
POST /articles
创建一篇文章
方法名,postArticlesOne
权限名,post:articles:one
POST /articles/batch
创建多篇文章
方法名,postArticlesBatch
权限名,post:articles:batch
PUT /articles/{articleId}
根据articleId全部更新一篇文章
方法名,putArticlesOne
权限名,put:articles:one
POST /articles/batch
全部更新多篇文章
方法名,postArticlesBatch
权限名,post:articles:batch
PATCH /articles/{articleId}
根据articleId局部更新一篇文章
方法名,patchArticlesOne
权限名,patch:articles:one
POST /articles/batch
局部更新多篇文章
方法名,postArticlesBatch
权限名,post:articles:batch
DELETE /articles/{articleId}
根据articleId删除一篇文章
方法名,deleteArticlesOne
权限名,delete:articles:one
POST /articles/batch
根据articleId删除多篇文章
方法名,postArticlesBatch
权限名,post:articles:batch
POST /articles/filter
创建filter临时资源,返回过滤结果,针对复杂条件查询场景
方法名,postArticlesFilter
权限名,post:articles:filter
使用 HTTP 状态码表示操作结果
常见 HTTP 状态码
1xx,信息状态码
表示服务器已接收请求并且客户端应继续请求。
| Status Code | Reason Phrase | 解释 | 场景 |
|---|---|---|---|
| 100 | Continue | 表示客户端可以继续其请求 | |
| 101 | Switching Protocols | 服务器已理解客户端的请求,并将通过Upgrade消息头通知客户端采用不同的协议来完成这个请求 | 用户打开聊天应用,应用通过HTTP协议加载初始页面。用户点击连接按钮,前端JavaScript代码创建WebSocket连接。在这个过程中,浏览器首先发出HTTP请求来切换协议,服务器返回101状态码表示切换成功,然后开始使用WebSocket协议进行双向通信。这种协议切换对于需要低延迟、实时双向通信的应用场景非常有用,比如聊天室、实时游戏、股票行情推送等。 |
2xx,成功状态码
表示请求已成功被服务器接收、理解并处理。
| Status Code | Reason Phrase | 解释 | 场景 |
|---|---|---|---|
| 200 | OK | 请求成功,并返回所请求的资源 | 通用成功状态码。 |
| 201 | Created | 请求成功并在服务器上创建了新的资源 | POST 请求在服务器写入了一条数据 |
| 202 | Accepted | 请求已接受,但尚未处理完毕 | 客户端请求启动一个异步大文件处理任务,服务器返回任务 taskId、taskStatus 和 taskUrl。 |
| 204 | No Content | 请求成功,但没有内容返回 |
3xx,重定向状态码
表示请求需要进一步操作才能完成。
| Status Code | Reason Phrase | 解释 | 场景 |
|---|---|---|---|
| 301 | Moved Permanently | 请求的资源已被永久移动到新位置 | |
| 302 | Found | 请求的资源临时从不同的URI响应请求 | |
| 304 | Not Modified | 资源未修改,自上次请求后未更改,因此可以使用缓存的版本 |
4xx,客户端错误状态码
表示客户端可能发生了错误,阻碍了服务器的处理。
| Status Code | Reason Phrase | 解释 | 场景 |
|---|---|---|---|
| 400 | Bad Request | 服务器无法理解请求的格式 | |
| 401 | Unauthorized | 请求需要用户认证 | |
| 403 | Forbidden | 服务器理解请求但拒绝执行 | |
| 404 | Not Found | 请求的资源未找到 | |
| 405 | Method Not Allowed | 请求的方法被禁用 | |
| 409 | Conflict | 请求与服务器的当前状态冲突 |
5xx,服务器错误状态码
表示服务器在处理请求时发生了错误。
| Status Code | Reason Phrase | 解释 | 场景 |
|---|---|---|---|
| 500 | Internal Server Error | 服务器遇到未知错误 | |
| 501 | Not Implemented | 服务器不支持请求的功能 | |
| 502 | Bad Gateway | 服务器作为网关或代理时,从上游服务器接收到无效响应 | |
| 503 | Service Unavailable | 服务器当前无法处理请求,通常是由于过载或维护 | |
| 504 | Gateway Timeout | 服务器作为网关或代理时,未及时从上游服务器收到响应 |
REST 对 HTTP 状态码的使用
GET - 用于获取资源。
200 OK: 请求成功,资源被返回。204 No Content: 请求成功,但没有内容返回(比如查询没有结果)。301 Moved Permanently: 资源已永久移动到新位置。302 Found: 临时重定向。304 Not Modified: 资源未修改,可以使用缓存的版本。404 Not Found: 请求的资源不存在。406 Not Acceptable: 无法提供客户端接受的响应格式。
POST - 用于创建新的资源。
201 Created: 新资源成功创建,通常与Location头部一起返回新资源的URI。202 Accepted: 请求已被接受,但尚未完成。400 Bad Request: 请求无效或格式错误。401 Unauthorized: 请求未经授权。403 Forbidden: 服务器理解请求但拒绝执行。409 Conflict: 请求与现有资源冲突。415 Unsupported Media Type: 请求的媒体类型不被服务器支持。
PUT - 用于更新现有资源或创建新资源(如果资源不存在)。
200 OK: 资源更新成功。201 Created: 新资源创建成功。204 No Content: 资源更新成功,但没有内容返回。400 Bad Request: 请求无效或格式错误。401 Unauthorized: 请求未经授权。403 Forbidden: 服务器理解请求但拒绝执行。404 Not Found: 请求的资源不存在。409 Conflict: 请求与现有资源冲突。
DELETE - 用于删除资源。
200 OK: 资源删除成功。202 Accepted: 请求已被接受,但尚未完成。204 No Content: 资源删除成功,没有内容返回。400 Bad Request: 请求无效或格式错误。401 Unauthorized: 请求未经授权。403 Forbidden: 服务器理解请求但拒绝执行。404 Not Found: 请求的资源不存在。
PATCH - 用于部分更新资源。
200 OK: 资源部分更新成功。204 No Content: 资源部分更新成功,没有内容返回。400 Bad Request: 请求无效或格式错误。401 Unauthorized: 请求未经授权。403 Forbidden: 服务器理解请求但拒绝执行。404 Not Found: 请求的资源不存在。409 Conflict: 请求与现有资源冲突。
HEAD - 与GET类似,但只返回头部信息,不返回响应体。
200 OK: 请求成功,头部信息返回。204 No Content: 请求成功,但没有内容返回。404 Not Found: 请求的资源不存在。
OPTIONS - 用于获取服务器支持的HTTP方法。
200 OK: 请求成功,返回允许的方法列表。204 No Content: 请求成功,但没有内容返回。
通用过滤条件
排序
先按照 starNum 升序排序,再按照 createdTime 降序排序。
风格一:
POST /articles/filter
{
"orderBy": ["+starNum", "-createdTime"]
}
分页
偏移分页
风格一:
POST /articles/filter
{
"page": 2,
"num": 10
}
风格二:
POST /articles/filter
{
"offset": 10,
"limit": 10
}
游标分页
POST /articles/filter
{
"scroll_id": 10,
"limit": 10
}
接口版本划分
- URI版本控制:如
/v1/users。 - HTTP头版本控制:通过请求头
Accept或Content-Type指定版本。
Ref
- https://docs.github.com/zh/rest
- https://www.ruanyifeng.com/blog/2014/05/restful_api.html
- https://www.ruanyifeng.com/blog/2018/10/restful-api-best-practices.html
- https://ics.uci.edu/~fielding/pubs/dissertation/top.htm
- OpenAPI,https://openapi.apifox.cn/
- RESTful API,https://restful.p2hp.com/
- https://github.com/bolasblack/http-api-guide
其他特殊情况,页面资源
- H5 页面中有私募频道页用于综合展示私募产品相关的信息
- 网站首页会展示各种产品、公司、经理等的综合信息
类似这种聚合页面(并不侧重某一大类数据资源的页面),采用如下设计方式:
- 首先确认该页面中的所有数据是否可以拆分为多个接口,例如PC首页大体上有产品信息和资讯信息,其他都是静态数据或其他可拆的小接口,整体上是可拆的
- 其次确认是否要有一个页面主接口,若一些页面有一些跟页面强相关的数据,可能需要专门为该页面开一个接口,例如 POST /ds/v1/homepage,若不需要,则全部拆分,优先采用前端服务端渲染而非后端手动聚合(下游服务调用上游服务,聚合优先放在下游)
- 最后确认是否可以复用原有接口,首页需要资讯数据,若可以复用 POST /ds/v1/news/filter 返回数据则复用,若不可复用,则优先使用 POST /ds/v1/pc/homepage/news/filter 形式
举例:
PC 端网站首页
- 可以拆分为 获取产品数据的接口 和 获取资讯数据的接口,整体可拆
- 无需主接口,使用服务端渲染方式聚合数据
- 不复用原有接口
| HTTP动词 | Endpoint | 描述 | 备注 |
|---|---|---|---|
| POST | /ds/v1/homepage/pc/products/filter | 查询多个资源 | 查询产品数据(PC主页专用) |
| POST | /ds/v1/homepage/pc/news/filter | 查询多个资源 | 查询资讯数据(PC主页专用) |