restful接口设计规范 写在前面 首先强调的一点是,RESTful不是一种规范,而是一种风格 ,它是不具有强制性的。
RESTful规范可以参考,但不能完全照搬。
风格这种东西说不准,公说公有理,婆说婆有理。而在开发团队中,最忌讳的就是不统一。所以我们首先确定以下基本原则:
新增、修改、删除、简单查询可以用RESTful
即简单的crud操作接口,按照RESTful规范
复杂的业务操作,数据同步操作,就按业务最适合的风格来写
比如批量操作,我们中间层很多服务业务很难简单抽象为一个名词。不要把时间精力放在纠结这逻辑到底是个什么资源
一、基本概念: REST是Representational State Transfer的缩写。直译过来是”表现层状态转化”。
其实这个词组前面省略了个主语–“Resource”,加上主语后就是“资源表现层状态转移”。
是不是每个词我都能看懂,但是放在一起不知道什么意思了?
资源(Resource)
网络中的一个实体。 例如,一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。
表现层(Representation)
“资源”是一种信息实体,它可以有多种外在表现形式。
比如一篇文章,可以使用XML、JSON、HTML的形式呈现出来
状态转化(State Transfer)
访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生”状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是”表现层状态转化”。
为了支持资源的REST化,业界提出了RESTful规范
二、RESTful规范 协议 生产环境,使用https协议
测试环境,目前的条件下使用http协议
路径 域名有两种设计
统一放在主域名下,用路径进行分组
放在子域名下
每个业务模块申请一个子域名,就太多了。所以我们使用第二种
我们的规范 url格式
1 {域名}/api/[page|inner|outer]/{模块名}/{功能名}
路径的约定:
按照restful规范,接口尽量使用名词,禁止使用动词,下面是一些例子。
1 2 3 4 5 6 7 8 GET /zoos:列出所有动物园 POST /zoos:新建一个动物园 GET /zoos/ID:获取某个指定动物园的信息 PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息) PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息) DELETE /zoos/ID:删除某个动物园 GET /zoos/ID/animals:列出某个指定动物园的所有动物 DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
反例:
1 2 3 /getAllCars /createNewCar /deleteAllRedCars
动作 http动词
方法
描述
幂等
GET
用于查询操作,对应于数据库的 select 操作
✔︎
PUT
用于所有的信息更新,对应于数据库的 update 操作
✔︎︎
DELETE
用于更新操作,对应于数据库的 delete 操作
✔︎︎
POST
用于新增操作,对应于数据库的 insert 操作
✘
HEAD
用于返回一个资源对象的“元数据”,例如数据的哈希或上次更新时间。或是用于探测API是否健康
✔︎
PATCH
用于局部信息的更新,对应于数据库的 update 操作
✘
OPTIONS
获取API的支持哪些方法。
✔︎
不要机械地通过数据库的CRUD来对应这些动词,很多时候,还是要分析一下业务语义。
关注幂等性
其中,PUT 和 PACTH 都是更新业务资源信息,如果资源对象不存在则可以新建一个,但他们两者的区别是,PUT 用于更新一个业务对象的所有完整信息,就像是我们通过表单提交所有的数据,而 PACTH 则对更为API化的数据更新操作,只需要更需要更新的字段
注意:PUT是要保障幂等的,所以不要在PUT中做+1这种更新。
关于PATCH的使用个人持保留意见。可讨论达成一致。
PATCH在RFC规范是是保障幂等性的,但是建议我们的代码设计上做到幂等。
我们的规范 建议只使用常用的动作
GET:查询
POST:除了查询外的所有
版本 考虑到我们的业务中需要支持多版本的情况几乎没有
如果以后需要支持多版本,在路径的最后加上版本v2…
比如
1 {域名}/api/[page|inner|outer]/{模块名}/{功能名}/v2
不带本版的默认是v1
过滤参数 在get请求中,规范常用的查询参数
分页:
?limit=10:指定返回记录的数量 ?offset=10:指定返回记录的开始位置。 ?page-current=2&page-size=100:指定第几页,以及每页的记录数。 ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
查询条件:
?animal-type=1:指定筛选条件 参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如, GET /zoo/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。
建议使用 /animals?zoo_id=ID格式
返回结果 服务器返回的数据格式,使用JSON
针对不同操作,服务器向用户返回的结果应该符合以下规范。
GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回删除的资源对象id
分页结果格式:
1 2 3 4 5 6 7 8 9 "data": { "records": [ {数据列表} ], "total": 0,//总数 "size": 10,//每页数据条数 "current": 1,//当前是第几页 "pages": 0//一共几页 },
状态码(Status Codes) 状态码范围
1 2 3 4 5 1xx 信息,请求收到,继续处理。范围保留用于底层HTTP的东西,你很可能永远也用不到。 2xx 成功,行为被成功地接受、理解和采纳 3xx 重定向,为了完成请求,必须进一步执行的动作 4xx 客户端错误,请求包含语法错误或者请求无法实现。范围保留用于响应客户端做出的错误,例如。他们提供不良数据或要求不存在的东西。这些请求应该是幂等的,而不是更改服务器的状态。 5xx 范围的状态码是保留给服务器端错误用的。这些错误常常是从底层的函数抛出来的,甚至开发人员也通常没法处理,发送这类状态码的目的以确保客户端获得某种响应。当收到5xx响应时,客户端不可能知道服务器的状态,所以这类状态码是要尽可能的避免。
服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 200 OK - [GET] :服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。201 CREATED - [POST/PUT/PATCH] :用户新建或修改数据成功。202 Accepted - [*] :表示一个请求已经进入后台排队(异步任务)204 NO CONTENT - [DELETE] :用户删除数据成功。400 INVALID REQUEST - [POST/PUT/PATCH] :用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。401 Unauthorized - [*] :表示用户没有权限(令牌、用户名、密码错误)。403 Forbidden - [*] 表示用户得到授权(与401 错误相对),但是访问是被禁止的。404 NOT FOUND - [*] :用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。405 Method not allowed 方法不被允许 (常见是服务端需要Get方法,但是客户端发起了POST请求)406 Not Acceptable - [GET] :用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。410 Gone -[GET] :用户请求的资源被永久删除,且不会再得到的。422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。500 INTERNAL SERVER ERROR - [*] :服务器发生错误,用户将无法判断发出的请求是否成功。502 网关错误503 Service Unavailable504 网关超时
错误处理 如果状态码是5xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。