如何设计 RESTful API

你一定听说过restful api,它是一种目前比较成熟且比较流行的互联网应用程序的API设计理论,注意,它是一种理论,而不是你必须遵守的规范。

本文尝试用最简单,最通俗的方式为你阐述这种理论并给出具体的设计示例,让你快速理解和掌握restful api 的设计方法。

1. 要名词,不要动词

restful api 的一个重要理念: 每个网址代表一种资源 , 既然网址是一种资源,那么就应当使用名词而不是动词,因为资源只能用名词来表达。比如用网站的用户就是一种资源,如果使用非restful api 来设计,大概率将会设计成下面的样子

url 方法 功能
/adduser post 增加用户
/getuser post 获取用户信息
/getallusers get 获取全部用户信息
/updateuser post 修改用户信息
/deluser post 删除用户

这种api 设计方法,url里会加入动词来表明这个url将实现的操作,咋一看起来意图十分明确。

而采用restful api 设计理论所设计出来的url则不需要动词,而是用名词,并且用名词的复数形式

url 含义
/users 用户集合
/users/22 id为22的用户

你注意到了,这种restful api 的url里没有动词了,那么我想新增,修改,查询,删除一个用户,该怎么办呢,别急,下面就介绍restful api 的第二个重要理念。

2. 用http method 来表示动作

http 自身有8中请求方法,其中有5个动词可以作为对资源的具体操作

请求方法 含义
GET(SELECT) 获取资源(一项或多项)
POST(CREATE) 新建资源
PUT(UPDATE) 更新资源(客户端提供改变后的完整资源)
PATCH(UPDATE) 更新资源(客户端提供改变的属性)
DELETE(DELETE) 删除资源

这里先解释一下put和patch的区别,他们都实现了更新,区别是patch请求只想服务端发送资源发生改变的某个属性,而put则是将资源改变后的完整数据都发送给服务器,就算这一次只改变了user的name,但是其他属性比如age,height也都要发送。

put,patch与post请求一样,也可以通过body发送请求数据,使用方法是完全相同的。用http 的 请求方法来代替传统的url里的动词,增删改查的url该如何设计呢?请看下表

url method 含义
/users get 获取资源集合
/users post 新增资源
/users/22 put/patch 更新id为22的用户信息
/users/22 delete 删除id为22的用户信息
/users/22 get 获取id为22的用户信息

看完上面的设计,有没有觉得restful api 看起来非常清爽,如果严格准守了这种设计理论,那么会有一个意想不到的效果,你能通过api猜出很多用价值的信息,比如下面的url

/classes/2/students/23

这个url里有两部分,前面/classes/2 表示id为2的班级,后面students/23表示id为23的学生资源,如果你现在想获取所有班级的信息,你应该向 /classes 发送get请求,这样就拿到了所有的班级信息,如果你想获取3班所有学生的信息,你应该向/classes/3 发送get请求。

3. 用http 状态码标识响应状态

使用http 状态码标识响应状态,我个人认为还存在争议,这个设计理念是讲,服务器在处理请求时,如果成功了,就返回一个code 为200的响应,如果失败了,是服务端导致的则返回500,如果是请求数据有问题则返回401,不要统统的都返回200,然后在返回数据里标识响应状态,比如下面是大家经常使用的一种响应格式

{
    'status': 203,
    'message': '缺少name参数'
}

或者

{
    'code': 508,
    'message': '服务器异常'
}

为了方便请求端处理响应,记录下服务端的响应数据,我们习惯在返回数据里定义code 或者status并自定义每一个编码的含义,这的确很方便通过日志追查问题,但这样做并不符合restful api 的设计理念,restful api 的理念是http code 自身就已经能够标识服务器响应的状态,不需要我们画蛇添足的在body里通过响应数据来标识。

就实践经验来说,我真心喜欢restful api 的url设计风格,但是让我放弃在返回数据里标识服务器处理状态却是有些为难,毕竟,在多个系统间进行交互,能够通过响应数据留下一些关键信息的日志对于追查问题来说太重要了。

4. 过滤信息

前面提到向/users 发送get请求可以获得user资源的全部数据,但我们几乎不会这样做,因为数据量太大的时候,你不可能将所有数据都返回,因此需要一些过滤措施

  1. ?limit=10 返回指定数量的资源
  2. ?offset=10 指定返回记录的偏移位置
  3. ?page=2&per_page=10 指定第几页,每一页需要返回多少数据
  4. ?sortby=name&order=asc 指定以哪个字段排序,升序还是降序
  5. ?user_type=1 指定筛选条件

在从资源集合里获取特定资源时,url允许带上query参数部分。

5. 其他的规范

  1. 将api 的版本放在url里,比如http://api.coolpython.net/v1/users,你也可以将版本放在headers里,自定义一个首部
  2. API与用户的通信协议,总是使用https协议,这一条我觉得这不重要,
  3. 尽量将api部署在专用域名下,https://api.coolpython.net 或者 https://coolpython.net/api, 这一点我也认为不重要

个人观点,设计 restful api ,最重要的有两条,一个是要名词,不要动词,另一个用http method 来表示动作,只需要做到这两点,就能够设计出结构合理易于理解和使用的接口。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案