restful最佳实践--接口规范
为了前后端分工明确,对接流畅,确保可读性和扩展性以及高可用、一致性,特约定下述无状态RESTful API规范:
简述
前后端分离意味着,前后端之间使⽤ JSON 来交流,两个开发团队之间使⽤ API 作为契约进⾏交互。从此,后台选⽤的技术栈不影响前台。当我们决定需要前后端分离时,我们仍然还需要⾯对⼀系列的问题:
- 是否⾜够的安全?我们怎么去存储⽤户数据,使⽤ LocalStorage 的话,还要考虑加密。采⽤哪种认证⽅式来让⽤户登录,并保存相应的状态?
- 是否有⾜够的技术来⽀撑前后端分离?有没有能⼒创建出符合 RESTful 风格的API?
- 是否有能⼒维护 API 接口?当前端或者后台需要修改接⼜时,是否能轻松地修改?前端和后台两个团队是不是很容易合作?是不是可以轻松地进⾏联调?
- 前后端职责是否能明确?即:后台提供数据,前端负责显⽰。
- 是否建⽴了前端的错误追踪机制?能否帮助我们快速地定位出问题。
前后端分离的核⼼:后台提供数据,前端负责显⽰
前提
RESTful API 统一约束客户端和服务器之间的接口。简化和分离系统架构,使每个模块独立!
- 请求中使用URI定位资源
- 用HTTP Verbs[动词](GET、POST、PUT、DELETE)描述操作(具体表现形式)
- 数据传递(默认)采用:
Content-Type: application/json; charset=utf-8
Rest
REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。REST是设计风格而不是标准。REST通常基于使用HTTP,URI,和XML(标准通用标记语言下的一个子集)以及HTML(标准通用标记语言下的一个应用)
统一接口(Uniform Interface)
统一接口约束定义客户端和服务器之间的接口。它简化了分离的结构,使各部分独立发展。
无状态(Stateless)
REST要求状态要么被放入资源状态中,要么保存在客户端上。或者换句话说,服务器端不能保持除了单次请求之外的,任何与其通信的客户端的通信状态。从客户端的每个请求要包含服务器所需要的所有信息。这样做的最直接的理由就是可伸缩性—— 如果服务器需要保持客户端状态,那么大量的客户端交互会严重影响服务器的内存可用空间(footprint)。
缓存(Cachable)
服务器返回信息必须被标记是否可以缓存,如果缓存,客户端可能会重用之前的信息发送请求。
客户-服务器(Client-Server)
客户端无需关注数据存储,服务器端无需关注用户界面,提高了前后端可移植性。
分层系统(Layered System)
客户端不关心直接连接到最终服务器还是连接到中间服务器。中间服务器可以通过启用负载平衡和提供共享缓存来提高系统可扩展性。分层系统也可以执行安全策略。
支持按需代码(Code on Demand,可选)
服务器可以通过传输逻辑来临时扩展或定制客户端的功能。
URL规范
GET https//domain.com/api/{模块名}/{?菜单名}/{接口名}/:param
-
不能使用大写,用中横线 - 不用下划线 _ ;
-
使用名词表示资源集合,使用复数形式(为确保所有API URIs保持一致),不能使用动词;
-
每个资源都至少有一个标识它的URI,同时应该遵循一个可预测的层次结构来提高可理解性,从而提高可用性;
-
无需在URI中增加版本号,通过HTTP请求头信息的字段中进行区分(或者在URI包含主版本信息,同时请求头包含子版本信息。
Accept: vnd.example-com.foo+json; version=1.1 Accept: vnd.example-com.foo+json; version=2.0
Request
请求方法 | 说明 | 安全性 | 幂等性 |
---|---|---|---|
GET(SELECT) | 获取资源 | ✔️ | ✔️ |
POST(CREATE) | 创建资源 | ❌ | ❌ |
PUT(UPDATE) | 替换(新增或完整更新) | ❌ | ✔️ |
DELETE(DELETE) | 删除资源 | ❌ | ✔️ |
PATCH | 是对 PUT 方法的补充,用来对已知资源进行局部更新 。 | ❌ | ✔️ |
OPTIONS | 用于url验证,验证接口服务是否正常 | ✔️ | ✔️ |
TRACE | 回显服务器收到的请求,主要用于测试或诊断 | ✔️ | ✔️ |
说明:
-
安全性 :不会改变资源状态,可以理解为只读的;
-
幂等性 :执行1次和执行N次,对资源状态改变的效果是等价的。
-
查询字段内容过多,统一使用POST方式查询,请求地址增加/query加以区分
-
批量删除,统一使用POST方式,请求地址增加/delete加以区分
由于存在批量删除的情况,而一些网关、代理、防火墙在收到DELETE请求后,会把请求的body直接剥离掉。建议将存在批量删除的接口统一改成POST提交,为了标识是删除操作,在请求路径上增加/delete。
GET
被用于获取资源。不允许对服务器上资源做任何修改操作。
示例:
GET http://www.example.com/customers/12345
GET http://www.example.com/customers/12345/orders
GET http://www.example.com/buckets/sample
PUT
常用于更新资源。通过请求体携带资源发送给服务器。注意:在资源ID由客户端而不是由服务器选择的情况下,也可以使用PUT来创建资源。修改成功返回200,创建成功返回201。建议使用post进行创建新资源。
PUT http://www.example.com/customers/12345
PUT http://www.example.com/customers/12345/orders/98765
PUT http://www.example.com/buckets/secret_stuff
POST
常用于创建新资源。创建成功通常返回201。
POST http://www.example.com/customers
POST http://www.example.com/customers/12345/orders
DELETE
删除资源。
DELETE http://www.example.com/customers/12345
DELETE http://www.example.com/customers/12345/orders
DELETE http://www.example.com/buckets/sample
HTTP Verb | /customers | /customers/{id} |
---|---|---|
GET | 200 (OK),customers列表。 可用于分页、排序、过滤。 | 200 (OK),单个customer。如果id不存在或非法,返回404 (NotFound)。 |
PUT | 404 (Not Found),除非你想更新整个资源 | 200 (OK) 或者204 (No Content)。如果id不存在或非法,返回404 (NotFound)。 |
POST | 201 (Created) | 404 (Not Found) |
DELETE | 404 (Not Found),除非你想删除整个资源 | 200 (OK) 。如果id不存在或非法,返回404 (NotFound)。 |
其他
-
排序
使用数组传递排序字段,-
表示降序,无任何标识表示升序。sorts: ['-age', 'name']
-
时间传递
日期和时间戳如果没有适当和一致地处理,可能是一个真正的头痛。建议使用UTC或GMT时间存储,处理,缓存等时间戳或者使用统一格式化的时间字符串”yyyy-MM-dd HH:mm:ss”
Respone
状态码
状态码 | 说明 |
---|---|
200 OK | 服务器成功返回请求的数据 |
201 CREATED | 新建或修改数据成功 |
202 Accepted | 表示一个请求已经进入后台排队(异步任务) |
204 NO CONTENT | 删除数据成功 |
400 INVALID REQUEST | 请求有错误,服务器没有进行新建或修改数据的操作(幂等操作) |
401 Unauthorized | 没有权限(令牌、用户名、密码错误) |
403 Forbidden | 得到授权(与401错误相对),但是访问是被禁止的 |
404 NOT FOUND | 请求记录不存在,服务器没有进行操作(幂等操作) |
406 Not Acceptable | 请求的格式不符合(比如用户请求JSON格式,但是只有XML格式) |
500 INTERNAL SERVER ERROR | 服务器发生错误,无法判断发出的请求是否成功 |
格式
- 前后端交互字段全部使用小驼峰方式
{
"code": "200", // HTTP响应码(好多javascript框架并不会获取http状态码,所以包装到body中便于使用)
"status": "success/fail/error", // 见下述表格
"content/data": []/{}, // 多条记录使用JSON数组,单条记录使用JSON对象
"message": [] // 状态为error或fail时,对应的错误信息
}
status说明
状态 | 说明 |
---|---|
fail | 返回码为 500-599 |
error | 返回码为 400-499 |
success | 其他状态码(1xx、2xx、3xx) |
---------------------------------------------------------------------------分割线-----------------------------------------------------------
1.后端应考虑接口请方式,现在使用统一方式post
2.后端定义前端传输字段要符合驼峰命名如:storeName;见名知意;后台返回字段也需符合驼峰命名
3.接口文档对应字段应和后端返回一致(后端可以小心点)
4. 后台返回数据字段需都在data里,不得在data之外;除与前端商定过
5. 后台返回值为空时,需返回相对应的键名如:{listData: null} (键值为空时值null)
6. 接口功能独立应分拆成新的接口
7. 返回字段须有msg,必须有值
{
"code":200, // 1.成功为200, 2.其他非200
"data": {
"id":"1001",
"name":"张三",
"age":"20"
},
"msg":"成功", // 1.成功信息, 2其他返回对应信息
}
示例
请求方式:POST
参数:说明
字段 | 类型 | 说明 | 必需 |
---|---|---|---|
token | string | token信息 | 是 |
oldPwd | string | 旧密码(hash) | 是 |
newPwd | string | 新密码(hash) | 是 |
返回值:
字段 | 类型 | 说明 | 必需 |
---|---|---|---|
code | int | 返回的状态码 | 是 |
data | Object/Array | 返回类型(值为空时返回null) | 是 |
msg | string | 返回信息 | 是 |
示例1
正确的
{
"code":200, // 1.成功为200, 2.其他非200
"data": {
"userInfo":{
"id":null, // 数据类型String,空时为null
"name":"张三",
"age":20 , // 数据类型为Number, 空时为null,
"imgUrls": ['地址1', '地址2'] //类型Array, 空时为null
"photoUrls": null
},
"level": :"领导"
},
"page": { //类型:Object 必有字段 备注:分页信息
"total":1, //类型:Number 必有字段 备注:总条数
"pages":1 //类型:Number 必有字段 备注:总页数
},
"msg":"成功", // 1.成功信息, 2其他返回对应信息
}
错误的
{
"code":200, // 1.成功为200, 2.其他非200
"data": {
"userInfo":{
},
"other":{
"ohterIfno": "小李子"
}
},
"level": "领导", // 错误格式
"msg":"成功", // 1.成功信息, 2其他返回对应信息
}
网友评论