RESTful 接口设计规范-个人总结
以下接口规范为个人收集并总结,仅供参考。欢迎提供建议
使用名词,使用HTTP 请求方法
接口中不要出现动词,以及动作。
使用HTTP 请求方法作为动作的表达。常见的CRUD,在HTTP 中都有对应的方法,可参考developer.mozilla.org/zh-CN/docs/...
HTTP 请求方法
表格来自:www.runoob.com/http/http-m...
方法 | 描述 |
---|---|
GET | 请求指定的页面信息,并返回实体主体。 |
HEAD | 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头 |
POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。 |
PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
DELETE | 请求服务器删除指定的页面。 |
CONNECT | HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。 |
OPTIONS | 允许客户端查看服务器的性能。 |
TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
PATCH | 是对 PUT 方法的补充,用来对已知资源进行局部更新 。 |
有以下几种:GET
、POST
、PUT
、DELETE
和PATCH
。与资源相关联的端点的名称必须与应用的操作相关的HTTP方法对应。
text
// bad case
GET /get_users
POST /insert_users
PUT /modify_users
DELETE /delete_users
// good case
GET /users
POST /users
PUT /users
DELETE /users
日常开发中不一定能保证完全按照HTTP 请求方法的规范,但至少要保证请求方法使用GET
,其他使用POST
全部小写
RFC 3986 将 URI 定义为区分大小写,对于不同环境,大小写 URI 代表不同的路径,为了避免混淆,建议在 URI 中应始终使用小写字母。
bash
// bad case
/Users/12345
// good case
/users/12345
使用斜杠 /
表示层次关系
斜杠/
通常用于资源的所属关系,后面的属于前面的。这点类似面相对象语言中的.
例如:/users/{userId}/address/
,{userId}
属于users
,address
属于{userId}
使用连字符 -
提高可读性
如果有多个单词,首先建议是使用/
来区分层级关系,如果无法做到,则建议使用-
区分
bash
// bad case
/users/{userId}/pendingorders
// good case
/users/{userId}/pending-orders
不要使用下划线_
虽然可以用下划线_
代替连字符 -
作为分隔符,但有些应用程序的字体,下划线 _
字符可能会在某些浏览器或屏幕中部分被遮住或完全遮住。
为避免这种混淆,建议使用连字符 -
而不是下划线 _
。
bash
// bad case
/users/{userId}/pending_orders
// good case
/users/{userId}/pending-orders
不用扩展名
除非直接访问文件,或者下载文件,否则不要使用文件扩展名(例如.xlsx
)
如果需要指定内容的格式,建议使用Content-Type
。
bash
// bad case
/users/{userId}/pending-orders.xlsx
// good case
/users/{userId}/pending-orders
Content-Type:application/vnd.ms-excel;charset=utf-8;
单数还是复数
如果能唯一确定的资源,则用单数,如果不能,则用复数。
如果单复数都行的,则用复数,因为单数也是复数的特殊形式,为了保持兼容与统一,所以建议复数。
bash
// bad case
GET /user/{userId}
GET /users?sex=man
// good case
GET /users/{userId}
GET /users/{userId}/address
精简
减少无用信息,提高信息密度,
sql
// bad case
GET /service/api/search
// good case
GET /search
不要暴露服务器的技术栈
技术栈包括使用了服务器、中间件、开发语言以及目录和系统结构等。这些建议不要暴露出来,一方面是有安全隐患,另一方面接口使用方也不关心,冗余不符合精简原则
bash
// bad case
/cgi-bin/get_user.php?user=100
// good case
/users?user=100
多条件查询(搜索)
多条件查询时,要求使用?
拼接参数查询
例如:/users?location=shenzhen&sex=man
查找位于 shenzhen
的并且sex=man
的所有用户
到底用时/
还是?
- 只有一个筛选条件,并且能唯一确定对象,则用
/
,比如/users/{userId}
- 有多个筛选条件,并且不能唯一确定对象,尤其是多条件搜索的情况,就用
?
比如/users?sex=man
注意:只有一个筛选条件时,这里说的唯一,不仅包括单一对象,也包括集合
比如
- URL"/users/2023/"返回的页面为2023年的存档
- URL"/users/2023/10/"返回的页面为2023年10月的存档
bash
// bad case
GET /users/id={userId}
GET /users/{userId}?sex=man
// good case
GET /users/{userId}
GET /users?localtion=shenzhen&sex=man
例子
ruby
// 单个条件,唯一确定
https://www.zhihu.com/people/zhihusucks
https://space.bilibili.com/11083481
https://weibo.com/3266943013
// 多个条件(搜索)
https://www.zhihu.com/search?q=123&type=content
https://www.zhihu.com/search?q=123&type=people
分页与排序支持
分页与排序也当做筛选条件放在?
后面
GET /users?limit=10&offset=0
:分页,从0行开始返回10条。GET /users?limit=10&offset=0&sort=asc&order=title
:排序,返回按名称升序排列的文章记录。
如何表示列表
当没有层次关系时(如在列表中),应该使用分号之类的标点符号或者常见的逗号。
例如:/users/{id1},{id2}
访问多个用户资源。
前端路由与域名相同问题
有时候前端的路由地址会与后端的接口相同,会造成困扰、无法区分到底是前端路由还是后端的接口,调试的时候也会很麻烦。
所以建议有两种方案,后端接口后者统一加/api/,或者后端提供的接口统一使用二级域名api.xx.com
状态码
众所周知,HTTP有自己的状态码,比如下述列表,那我们自己的业务的错误码或者状态码,应该直接复用HTTP的状态码,还是自定义呢?
HTTP 状态码分类
表格来源:www.runoob.com/http/http-s...
HTTP 响应状态码由三个数字组成,第一个数字定义了状态码的类型。
响应分为五类:信息响应(100--199),成功响应(200--299),重定向(300--399),客户端错误(400--499)和服务器错误 (500--599):
分类 | 分类描述 |
---|---|
1** | 信息,服务器收到请求,需要请求者继续执行操作 |
2** | 成功,操作被成功接收并处理 |
3** | 重定向,需要进一步的操作以完成请求 |
4** | 客户端错误,请求包含语法错误或无法完成请求 |
5** | 服务器错误,服务器在处理请求的过程中发生了错误 |
结论:业务异常不要使用HTTP的状态码,而要统一放在HTTP 200状态码下面,使用自定义的业务状态码。原因如下
下述结论大部分来自:
如果业务状态码使用HTTP的状态码,那么会有如下问题
排查困难、分工混乱:一般HTTP的错误码(比如404)都是由服务器或者框架层面抛出的,并且大部分公司的运维与业务开发是分开的,运维只关心服务器层面的异常,业务开发只关心业务异常。如果不区分状态码,那么出现一个404的异常,就无法一眼看出来到底是谁的职责,还得进一步爬log,看错误信息才能确定问题,排查问题困难。如果区分的很清楚,则HTTP状态码的问题直接找运维,自定义的业务异常则直接找业务开发,再具体点就是HTTP200的异常去找业务开发,非HTTP200找运维。
扩展性差:HTTP那点状态码支撑不起众多的业务场景,比如404表示找不到,业务中存在多种找不到的情况,比如"用户找不到"、"订单找不到"、"商品找不到"等情况,如果都用404,那排查问题还是很困难。
运营商劫持:非200状态码(比如404、500)可能会被运营商劫持。或者其他开发人员除了200之外的状态码不认识,还有反向代理或者CDN都有可能有问题
接口出参格式
这里可参考Ant Design Pro的统一规范,这里截取一部分
tsx
{
"success": true,
"data": {},
"errorCode": "1001",
"errorMessage": "error message",
"showType": 2,
"traceId": "someid",
"host": "10.1.1.1"
}