API接口设计实践
查看Restful风格各种场景下接口示例请直接跳转接口设计部分
背景
前后端开发中,后端需要为前端提供接口。那么我们如何来提供一套相对优雅的接口设计实践?
我相信许多同学都要命名困难综合征,拍脑袋的给出接口又很难形成良好的实践,那尤其如果是多人共同开发的工程,很容易就形成各种风格各立的接口,增加了一丢的不易维护。那想当然我也是其中的一员…所以这里也是给出自己的一份实践和参考。
前后端接口设计通常是Http接口。Http接口设计目前比较流行的是Restful风格接口设计。
Restful
风格
得益于Http
协议的相对规范与通用,有了Restful
风格的接口设计。Restful
的思路是将请求视为对资源的操作,用HttpMethod
方法表示操作,Path
表示资源路径。下面我们会大概看一下各种场景下Restful
风格实践的一种思考。
Http
接口设计
关于Http
接口设计,我们基本遵循Restful
风格,当然Restful
风格不是银弹,有些情况下它并不适用,很容易挠头秃。这里我主要参考《Java
开发手册》和谷歌的API
设计指南。
设计约定
-
【强制】前后端交互的
API
,需要明确协议、域名、路径、请求方法、请求内容、状态码、响应体。- 协议:生产环境必须使用
HTTPS
- 路径:每一个
API
需对应一个路径,表示API
具体的请求地址:- 代表一种资源,只能为名词,推荐使用复数,不能为动词,请求方法已经表达动作意义。
URL
路径不能使用大写,单词如果需要分隔,统一使用~~下划线~~短横线1。- 路径禁止携带表示请求内容类型的后缀,比如"
.json
",“.xml
”,通过accept
头表达即可。
- 请求方法:对具体操作的定义,常见的请求方法如下:
GET
:从服务器取出资源。POST
:在服务器新建一个资源。PUT
:在服务器更新资源。DELETE
:从服务器删除资源。
- 请求内容:
URL
带的参数必须无敏感信息或符合安全要求;body
里带参数时必须设置Content-Type
2。
- 协议:生产环境必须使用
-
【强制】服务端发生错误时,返回给前端的响应信息必须包含 HTTP 状态码,
errorCode
、errorMessage
、用户提示信息四个部分。- 说明:四个部分的涉众对象分别是浏览器、前端开发、错误排查人员、用户。
- 用户的提示信息要求:简短清晰、提示友好,引导用户进行下一步操作或解释错误原因,提示信息可以包括错误原因、上下文环境、推荐操作等。
errorMessage
:简要描述后端出错原因,便于错误排查人员快速定位问题,注意不要包含敏感数据信息。- 错误码不能直接输出给用户作为提示信息使用。
-
【强制】HTTP 请求通过
URL
传递参数时,不能超过 2048 字节。说明:不同浏览器对于
URL
的最大长度限制略有不同,并且对超出最大长度的处理逻辑也有差异,2048字节是取所有浏览器的最小值。 -
【强制】
HTTP
请求通过body
传递内容时,必须控制长度,超出最大长度后,后端解析会出错。说明:
nginx
默认限制是 1MB,tomcat
默认限制为 2MB,当确实有业务需要传较大内容时,可以通过调大服务器端的限制。 -
【参考】在接口路径中不要加入版本号,版本控制在
HttpHeader
头信息中体现,有利于向前兼容。说明:当用户在低版本与高版本之间反复切换工作时,会导致迁移复杂度升高,存在数据错乱风险。
这里基本规范了接口设计及命名,还包含异常返回。但是我们也可以看到这种API
对操作单个资源比较友好,而我们很多场景会有批量、分页等,设置有些很难用对资源的操作来表达。那这里我们可以结合谷歌的API
设计来补充这些场景。
接口设计
这里我们以最常见的用户资源为例,以复数表达资源为users
3。
请求描述 | URI | 示例 | 备注 |
---|---|---|---|
创建一个用户 | POST /users | POST /users {“name”:“test”} | 通过body 传参 |
查询某个用户 | GET /users/{id} | GET /user/1 | GET 语义不支持传参body |
修改某个用户 | PUT /users/{id} | PUT /users/1 {“name”:“test”} | 通过body 传参 |
删除某个用户 | DELETE /users/{id} | DELETE /user/1 | DELETE 语义不支持传参body |
分页列取用户 | GET /users | GET /users?name=test | 比较通用的列取查询,比如按用户某个属性分页列取 |
我们可以看到Restful的风格可以提供的标准接口命名就是上面5种,基本上都是以一个资源的角度来设计的。但通常我们也会有些批量操作或者复杂的查询,更常见的则是我们可能会需要更细粒度的修改操作,比如启用/停用用户。
这些在谷歌API设计中视作为非标准方法,此时通常来说我们不可避免的会在URI中用动词来区分操作,谷歌推荐了一种使用":"分隔动作的风格。
请求描述 | URI | 示例 | 备注 |
---|---|---|---|
批量创建用户 | POST /users:mpost [{“name”:“test”}] | ||
批量查询用户(按id) | GET /users:mget | GET /users:mget?ids=1,2,3 | 性能要求使用批量接口,自定义方法 限制批量查询条数!最多2048字节 |
批量修改用户 | PUT /users:mput [{“id”:1}] | PUT /users 会有歧义,批量还是全部? | |
批量删除用户 | DELETE /users:mdel?ids=1,2,3 | DELETE /users 会有歧义,批量还是全部? 限制批量查询条数!最多2048字节 | |
复杂查询用户 | GET /users:serarch | GET /users:serarch?orgIds=1,2 | |
非简单属性修改(停用用户) | PUT /users/{id}:disable | PUT /users/1:disable | 标准的资源方法不合适表达语义, 如重启机器、发送邮件 (通常体现出状态、流程的变化) |
批量清空用户 | DELETE /users:clear | DELETE /users:clear | 自定义批量的一种case |
移动用户(组织下) | PUT /orgs/1users/{id}:move | PUT /orgs/1users/1:move {“orgId”:“2”} | 存在父子资源关系时的移动到另一个父级下 |
以上,批量的接口命名险些让我头秃…这里使用multi+post/get/put/delete
命名。
URL命名通常有三种,驼峰命名法(serverAddress),蛇形命名法(server_address),脊柱命名法(server-address)。由于URL是大小写敏感的,如果用驼峰命名在输入的时候就要求区分大小写,一个是增加输入难度,另外也容易输错,报404。蛇形命名法用下划线,在输入的时候需要切换shfit,同时下划线容易被文本编辑器的下划线掩盖。所以建议使用脊柱命名法(server-address)。Java开发手册建议使用下划线,而GoogleAPI建议使用驼峰。 ↩︎
默认
application/json;charset=UTF-8
↩︎两份文档都推荐使用复数形式,在写这篇文档之前我个人的习惯一直是单数(可能是受到表设计的影响)。不过综合比较各种实现和说法来看,我也建议使用复数形式表示资源(集合)。关于使用单数有另外2个场景:1. 单例资源或无复数形式,比如值对象或者1:1的关系;
2. 单数表示单个资源,复数表示复数资源。2不建议这么去玩,我们这里需要一个语义上的资源概念。 ↩︎