hello云胜

技术与生活

0%

restful接口设计规范

写在前面

首先强调的一点是,RESTful不是一种规范,而是一种风格,它是不具有强制性的。

RESTful规范可以参考,但不能完全照搬。

风格这种东西说不准,公说公有理,婆说婆有理。而在开发团队中,最忌讳的就是不统一。所以我们首先确定以下基本原则:

  1. 新增、修改、删除、简单查询可以用RESTful

    即简单的crud操作接口,按照RESTful规范

  2. 复杂的业务操作,数据同步操作,就按业务最适合的风格来写

    比如批量操作,我们中间层很多服务业务很难简单抽象为一个名词。不要把时间精力放在纠结这逻辑到底是个什么资源

一、基本概念:

REST是Representational State Transfer的缩写。直译过来是”表现层状态转化”。

其实这个词组前面省略了个主语–“Resource”,加上主语后就是“资源表现层状态转移”。

是不是每个词我都能看懂,但是放在一起不知道什么意思了?

资源(Resource)

网络中的一个实体。 例如,一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。

表现层(Representation)

“资源”是一种信息实体,它可以有多种外在表现形式。

比如一篇文章,可以使用XML、JSON、HTML的形式呈现出来

状态转化(State Transfer)

访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生”状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是”表现层状态转化”。

为了支持资源的REST化,业界提出了RESTful规范

二、RESTful规范

协议

生产环境,使用https协议

测试环境,目前的条件下使用http协议

路径

域名有两种设计

  1. 统一放在主域名下,用路径进行分组

    1
    example.com/api/XXX
  2. 放在子域名下

    1
    api.example.com/XXX

每个业务模块申请一个子域名,就太多了。所以我们使用第二种

我们的规范

url格式

1
{域名}/api/[page|inner|outer]/{模块名}/{功能名}

路径的约定:

  • 命名必须全部小写

  • 资源(resource)的命名必须是名词,并且必须是复数形式

  • 如果要使用连字符,建议使用中划线而不是下划线,下划线字符可能会在某些浏览器或屏幕中被部分遮挡或完全隐藏

    当然不建议使用连字符

  • 易读

按照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来对应这些动词,很多时候,还是要分析一下业务语义。

关注幂等性

其中,PUTPACTH 都是更新业务资源信息,如果资源对象不存在则可以新建一个,但他们两者的区别是,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 Unavailable
504 网关超时

错误处理

如果状态码是5xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。

1
2
3
{
error: "错误原因"
}