Restful设计
Restful设计
实践中的困惑
- 接口名称:通常会设计一个接口叫
/add
或者是/insert
, 后台对应数据的插入操作,但是从来没有互相约束为 一定叫add 还是叫insert - 不幂等的设计:有时候会对某个开关设计一个叫
/toggle
或者/switch
, 调用一次会将这个状态置为相反的状态,在网络出现问题时,会出现和预期不一致的状态 - 杂乱的状态码设计:我们永远无法预料到程序里所有可能出现的错误,但是我们总是能知道我们期望的正确结果
Restful 的设计哲学
Restful1 是由 Roy Thomas Fielding2 在2000 年的博士论文中提出的。
-
一切皆资源 这令人想到 Linux 的设计哲学:一切皆文件
-
每个资源由唯一URL标识
-
资源处理使用 POST, GET, PUT, DELETE方法对应 创建,读取, 更新,删除操作,此处对应数据库的CURD操作
-
幂等性 一个请求执行一次和执行N次的结果是一致的
-
状态码 使用Http Status
处理一些蛋疼的例子
- 前提:restful 是一种设计风格,并不是明确的规范
一切皆资源
- 系统通常有一个用户登录,我们设计为
/login
,但是设计为POST /sessions
就略显别扭,虽然这个在逻辑上是说的通的POST /session
笔者见过这样的实践
- 播放音乐的接口,理解为创建一个播放状态,可以变更为
POST /musics/{id}?status=play
, 修改某个音乐的状态为播放
方法动词
- 当url 严格设计为名词时, 使用 对应的请求方法,则表述性会更强
方法 | 操作 | 描述 |
---|---|---|
GET | 获取 | 查询,比如 GET /cars/231, 获取编号为231的车子信息 |
POST | 新建 | POST 请求是不幂等的,因为我们允许有多辆属性相同的车子 |
PUT | 更新 | 修改, 比如 PUT /cars/231, 修改标号为231 的车辆信息 |
DELETE | 删除 | 比如 DELETE /cars/231, 删除编号231 的车辆信息 |
GET 和 POST 区别
- GET只接受ASCII 字符且长度受限, POST 不受限
- GET 设计时考虑参数可读性,参数的数量
- GET 请求更加容易被浏览器刷新,要考虑幂等性
- GET 请求容易被缓存
实战建议
- 路径参数 (spring mvc 中 使用
@PathVariable
注解的参数)- 建议处理可枚举的参数, 因为对监控不太友好, 按路径可能会统计出很多指标;
- 但是比如在一个多租户的系统中, 用来传递租户id,恰好有自然被 监控系统发现并自动分组
- 关于路径参数中使用id 以符合 restful 风格的问题:在没有副作用的情况下使用,(可能作者当年也没有想到,互联网的数据是一个指数级的增长吧,作者当初也许是设想在一个 MIS 系统中,很自然的传递和查看一些数据吧)。
- queryString (spring mvc 中 使用
@RequestParameter
注解的参数)- 参考意见是: 如果一个地址很方便的被 使用者 copy/paste 进行传递,那么那建议 使用
GET + QueryString
的组合。比如,在监控系统中, 把查询条件在 queryString 中 传递,方便使用中,传递信息,打开浏览器 接收者便能直接查看到问题所在,而不是按照 对方所说的设置查询参数。 比如 kibana 和 grafana 的 url 通常都是这样很长的。 - 要注意GET URI 字符受限问题,比如某个资源的翻页查询,通常只有有限的5~6 个参数,不妨使用
GET + QueryString
组合。 - 如果查询条件过于复杂,服务器不能提供响应,不妨改为POST + 202 的异步组合, facebook 的 广告数据接口有类似的设计
- 参考意见是: 如果一个地址很方便的被 使用者 copy/paste 进行传递,那么那建议 使用
使用正确的状态码
- 比如404, 表示接口找不到呢还是资源接口不存在?
- 如果是被解读为资源不存在,那就是一个业务问题,对用户来说是能理解的,是合情合理的
- 如果是被解读接口不存在,这就是一个技术问题,是应该消灭在开发阶段的, 而不应该暴露在生产环境的。
- 综上, restful 原本就是 表示资源的,属于业务范畴,用404 表示资源不存在也是合理的。如果404 被解读成了接口找不到,那就是开发的问题。按照时间线来说,现有接口,后提供资源服务。
-
状态码会不会不够用?绝大部分情况下,一个REST API 总是返回一个甚至2个期望的结果,但是出现异常的情况却有很多,所以与其期望明确定义好每一种异常情况,不如做好异常业务状态的提示信息的用户体验
状态码 说明 1xx 消息 2xx 成功 3xx 重定向 4xx 错误请求 5xx 服务器错误
Richardson Maturity Model
级别 | 说明 |
---|---|
Level 0 | The swarmp of POX |
Level 1 | Resources |
Level 2 | HTTP Verbs |
Level 3 | Hypermedia Controls |
这个不展开细说,可能我们大部分的API 处于 1~2 或 2~3 的一个中间状态, 尝试了就是最好的。
我想说的是,不管restful 风格在实践过程中有多少 争议,既然存在这么一个 非标准的准则
, 作为开发就应该去布道,思考,并反复实践,抽象我们的业务,使我们的API 尽量去符合和遵循这样一个东西。
总结
- 如果你不熟悉Restful, 还是多在各种业务场景下练习抽象思维
- 如果某个业务场景不适合使用Restful, 可以试试变通的抽象
- 跟详细的定义http 定义可以 阅读 rfc7231, 其中详细描述了各请求状态码的含义