Restful API 规范
很早的时候接触Restful的时候,觉得其标准很不错; 但是在实际项目中发现, 大部分程序员甚至资深的工程师都没有类似的意识。
接触久了发现可能是受国内大厂影响。 很多大厂的开放API的接口也完全没有考虑Restful的标准。 甚至很多接口的URL定义为一个,通过POST的请求体里面的参数来区分不同的接口。
但是针对新的开发项目来讲,一套标准的规范可以有效的增加可读性,提供协同效率。
这里主要讲述URL 规范, 因为请求方法比较简单, 实际项目中保持如下使用即可:
- GET - 获取资源
- POST - 新增资源
- PUT - 对资源的修改,(有些类似流程的操作,也相当于是资源的修改,建议采用PUT)
- DELTE - 删除资源
资源路径: URL
一个好的URL具有很强可读性的,具有自描述性, 加上 动词 Http Method 可以很清晰的表达出接口的含义。
例如:GET /orders/1001/items/1
代表获取 订单1001的子订单1的信息。 我们拆解分析如下:
- .
orders
- 名词的复数,代表一个集合和一个池子,也可以理解为DDD中的领域- .
1001
- 一个集合后面的{ID} 或者说标识
表示这个集合中的1001这个这个对象- .
items
- 第二个名词复数,代表了一个子集合,这个 集合率属于orders:1001
- .
1
- 标识items
这个集合下标识为1
的这个对象
- .
- .
- .
这里的标识一般代表能从一个集合找到一个唯一对象的主键。 同理推断:
POST /orders
则代表向order集合新建对象。
/版本号/资源路径
/v1/users/{userId} //URL 可以定位到具体的用户
/v1/users?[&keyword=xxx][&enable=1][&offset=0][&limit=20] // URL 可以定位到一个用户集合
版本号
命名版本号可以解决版本不兼容问题,在设计 RESTful API 的一种实用的做法是使用版本号。一般情况下,我们会在 url 中保留旧版本号,并同时兼容多个版本
GET /v1/users/{userId} // 版本 v1 的获取id为{userId}的用户
GET /v2/users/{user_id} // 版本 v2 的获取id为{userId}的用户
资源路径
URI 严格意义不能包含动词,只能是名词(命名名词的时候,要使用小写、数字及下划线来区分多个单词)。
资源的路径应该从根到子依次如下:
/{resources}/{resource_id}/{sub_resources}/{sub_resource_id}/{sub_resource_property}
标准示例
GET /users # 查询用户信息列表
GET /users/1001 # 查看某个用户信息
POST /users # 新建用户信息
PUT /users/1001 # 更新用户信息(全部字段)
PATCH /users/1001 # 更新用户信息(部分字段), 一般不用, 用PUT即可
DELETE /users/1001 # 删除用户1001
// 子目录示例
POST /users/{user_id}/roles // 添加用户的角色
请求方法的延伸
当一个资源变化难以使用标准的 RESTful API 来命名, 或者说容易混淆; 比如”密码重置“ 、 ”修改密码“。这种场景可以考虑用 PATCH /users/1001
但是URL可读性就有所丢失。这个时候我一般会在URL末尾用动词
来延伸从而保证URL的可读性。
一般我会采取下面两种方式:
- 通过添加 ’/actions/{action}';
PUT /users/1001/actions/reset-pwd
代表重置用户1001的密码 - 通过添加 '_{actioin}';
PUT /users/1001/_reset-pwd
代表重置用户1001的密码
两种方式采取一种保持一致。
查询参数
RESTful API 接口应该提供参数,过滤返回结果。
GET /{version}/{resources}/{resource_id}?offset=0&limit=20
响应体
针对响应体,标准Restful是通过HttpStatus来标识返回结果的成功和失败,如果是成功返回,直接返回实际业务体。 但是国内大部分项目都会在响应体外面包一层,通过自定义的方式来标识成功和失败。 比如以下格式:
{
"code": 0,
"msg": "success",
"data": {
"name": "Joe"
}
}
如果是直接返回data, 就需要结合状态码来操作。 如果是data外面包一层, 无论成功失败都返回
status 200
, 具体成功失败需要根据响应体的code
字段来判断。
重点强调几点
- 尽量返回修改后的内容,比如新增和修改接口,返回修改后新的内容
- 点赞、收藏等操作也同样需要返回修改后的内容,方便客户端局部更新,避免刷新页面
状态码
使用适合的状态码很重要,而不应该全部都返回状态码 200
状态码,可根据以下标准按照项目扩展自身状态码:
200~299段 表示操作成功:
200 操作成功,正常返回
201 操作成功,已经正在处理该请求
300~399段 表示参数方面的异常
300 参数类型错误
301 参数格式错误
302 参数超出正常取值范围
303 token过期
304 token无效
400~499段 表示请求地址方面的异常:
400 找不到地址
500~599段 表示内部代码异常:
500 服务器代码异常
关于对标准的反思
下面是来自百度百科对标准的定义:
标准是对重复性事物和概念所做的统一规定,它以科学技术和实践经验的结合成果为基础,经有关方面协商一致,由主管机构批准,以特定形式发布作为共同遵守的准则和依据。
如果大部分人都没有按标准来, 那他还是个标准吗? 曾经在一个大几千万的项目中, 第三方对接我团队的接口时候很诧异的问我,为什么有PUT请求。 后面还私下和业主方表达了我们接口的不专业。 我们在对接携程的接口的时候,发现接口就一个URL,POST请求。 大部分知名的国内平台最多就两个请求方法: POST 和 GET, 而且URL上无法定位到资源。
反思过后,只能说一致性 > 标准
, 大家都一致了, 非标准的也就标准
了。
标准Restful的好与坏
万物都有两面, 如果对其缺点要求很高可以选择性遵循。
优点
- API接口可读性, 请求方法 + URI 可以很明确的知道接口是做什么的
- 减少不必要的DTO;有些标识在URI上已经有了,就没有必要写DTO了。 (很多项目中,每个接口一个请求DTO,看着头大。)
缺点
缺点就是不方便监控。
非标准的URL干同一类的事情URI是固定的, 比如:删除用户 POST /deleteuser
具体哪个用户在请求体, 这样就很方便统计监控这个API,因为URL没变。
而标准的Restful的URL会是这样的:DELETE /users/1001
, 删除不同用户的地址的URL是不同的, 监控统计会有些麻烦。
不方便监控, 不代表不能监控,取决于监控平台对URI的支持力度。