api 接口有必要 restful 吗?

本贴最后更新于 1684 天前,其中的信息可能已经时移俗易

REST 的起源?

REST 流行起来是因为 Roy Fielding 在他 2000 年的博士论文 Architectural Styles and the Design of Network-based Software Architectures 中介绍并推荐了它。Roy 因为他对 web 开发,特别是 HTTP 规范的贡献而闻名。

什么是 RESTful APIs ?

REST 即表述性状态转移,是一种用于构建可扩展 web 服务的架构风格。Roy 提倡使用他在 HTTP 标准 中参与制定的请求方法来赋予 HTTP 请求语义。

因此当使用 REST 时,下面的 HTTP 请求都有不同的语义:

  • GET /object/list
  • POST /object/list
  • PUT /object/list

这只是一部分 HTTP 请求方法。完整的列表如下:CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, TRACE。可以原谅你从没有听说过其中的某些方法,因为它们中的一部分也一直都没得到客户端或服务端的支持。
Roy 同样主张使用 他参与制定的 HTTP 响应状态码来传达响应的语义。共有 38 种 HTTP 响应状态码。

Method Method
200 OK 201 Created
202 Accepted 203 Not authorized
204 No content 205 Reset content
206 Partial content
300 Multiple choice 301 Moved permanently
302 Found 303 See other
304 Not modified 306 (unused)
307 Temporary redirect
400 Bad request 401 Unauthorized
402 Payment required 403 Forbidden
404 Not found 405 Method not allowed
406 Not acceptable 407 Proxy auth required
408 Timeout 409 Conflict
410 Gone 411 Length required
412 Preconditions failed 413 Request entity too large
414 Requested URI too long 415 Unsupported media
416 Bad request range 417 Expectation failed
500 Server error 501 Not implemented
502 Bad gateway 503 Service unavailable
504 Gateway timeout 505 Bad HTTP version

由于各种原因,RESTful API 可能比上面显示的更复杂。

RESTful APIs 已知的主要问题

在许多方面,例如内容交付上,REST 都是一个不错的机制,它也很好得为我们服务了二十多年。但是是时候打破沉默了,承认 RESTful API 可能是在 web 软件中广泛使用的最糟糕的想法之一。

我们很快将看到更好的构建互联网 API 的方式,但在完全领会这个解决方案之前,我们首先得知道 RESTful APIs 的 5 个主要问题,它们使 API 昂贵,冗余,并且容易出错。

问题 #1: 关于 RESTful API 是什么,几乎达不成共识

没有人能够在全部的请求方法,实体和响应状态码的真正含义上达成一致。
设想一下,例如,我们应该用 200 OK 表示成功的更新了一条记录还是要用 201 Created?看上去我们应该用 250 Updated,但是这个状态码并不存在。而且谁能向我解释一下 417 Expectation failed 的真正含义。当然指除了 Roy 之外的人。

HTTP 方法和响应状态码的词汇过于模糊和不完整,无法就其语义达成一致。至少在我所知道的,没有任何管理机构能召集大家一起把事情讲清楚。一个公司定义的 200 OK 必然与另一家公司定义的有细微且令人讨厌的区别。这意味着一个 RESTful 模式不能像我们想要的那样可以预测。

问题 #2: REST 词汇的支持不完全

即使就 REST 的各个语义达成一致,我们还会遇到另一个实际的问题:大部分客户端和服务器程序不能完全支持 HTTP 协议 里定义的动词或响应状态码。例如,大部分浏览器对 PUTDELETE 仅提供有限的支持。并且很多服务端程序通常也不能正确地实现这些方法。

那么我们如何突破这些限制呢?一种常用的方法是在浏览器的表单里嵌入预期的请求方法。这意味着 REST 请求现在包含:

  • 一个 HTTP 请求方法, 如 POST
  • 一个请求地址, 如 /object/list
  • 一个我们实际期望的方法,嵌入在请求的实体中,如 DELETE
  • 请求实体本身,如表单字段数据

响应状态码的处理也好不到哪去。不同的 web 浏览器 (或者服务器) 对响应状态码的解释也经常各不一样。例如,如果遇到 307 Temporary redirect 状态码,一个浏览器可能允许客户端 JavaScript 在重定向之前处理响应并取消它,另一个浏览器可能就禁止客户端 JavaScript 处理响应。所有程序中真正可靠的响应状态码只有 200 OK500 Internal server error。除此之外,支持程度一个比一个糟糕。因此,我们也常常可以看到在返回的的实体里嵌入有实际想要的响应状态码。
当然,即使我们能让每个人都对什么是 REST 达成一致,并神奇地修复所有已连入互联网的程序对 REST 支持的缺陷。

问题 #3: REST 词汇对 APIs 来说不够丰富

REST 的方法和响应状态码太过于局限,无法有效地表示所有应用程序所需的各种请求和响应。想一下,我们创建了一个应用程序,并要向 HTTP 客户端 发送一个 “render complete” 响应。我们不能使用 HTTP 响应状态码,因为它不存在并且 HTTP 不能扩展。

还有另外一个问题:HTTP 响应状态码 (200, 201, 202 等) 是和 HTTP 请求方法 (GET, POST 等)没有直接联系的数字,而我们的实体载荷通常是 JSON 格式。所以执行 REST 请求就好像发送一个目的地是斯瓦希里却包含英语内容的包裹,并通过打鼓声来获得交付确认。这种复杂性往往会造成巨大的困惑和错误。它给我们带来了下一个问题:调试。

问题 #4: RESTful APIs 非常难调试

如果你曾经用过 RESTful API,你大概知道他们几乎难以调试。这是因为我们必须查看 7 个不同的地方来整理出一个完整的事务中发生了什么:

  • HTTP 请求方法, 如 POST
  • 请求地址, 如 /object/list
  • 请求实体中嵌入的实际期望的方法,如 DELETE
  • 请求实体中的实际数据,如 表单数据
  • 响应状态码,如 200 OK
  • 响应实体中嵌入的实际期望的响应状态码,如 206 Partial content
  • 响应实体中的实际数据

我们的问题不仅有两个 REST 词汇上的限制,大家就语义达不成一致。现在还有需要查找 7 个不同的地方才可能完全理解和调试一个事务。唯一还能使这更糟的事是,REST 完全绑定到了一个协议上,而不适用于任何其他的协议。当然,这也是我们的下一个问题。

问题 #5: RESTful APIs 通常绑定在 HTTP 上

RESTful API 打破了良好通信的一个基本原则:消息内容应该独立于传输通道。将两者混合来迷惑读者是一个历史悠久的方法。

将 HTTP 协议 与事务的意义混合使得 RESTful API 完全不可移植。将 RESTful API 从 HTTP 迁移 到其他传输协议上需要从 7 个不同的地方解构和重组信息,我们再使用这些信息对 RESTful 请求的全部含义进行编码。

  • RESTful

    一种软件架构设计风格而不是标准,提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

    30 引用 • 114 回帖 • 2 关注
1 操作
ghostsf 在 2020-04-24 10:16:15 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • wangjieguang

    必须啊,get,post,delete,很简洁

  • 88250

    能用就用,不必强求。重要的是尽量统一风格,不要混用。另外查询类接口可以试试 GraphQL。

    1 回复
  • ghostsf

    😂 舒坦了。在用 GraphQL 了。

  • someone27889 1 评论

    理想模式时 /users/1 我能查到 第一页的所有用户/user/id123 能查到 id123 的用户 /user Put 能新增 /userPost 能修改 /user/id123 Delete 能删除.但是往往会有一些着急/或者难搞的 action 打乱你的阵型

    1 回复
    post 新增 put 更新
    nobt 1 1 赞同
  • Blackman99

    只要约定好规则并严格遵守,用什么不重要

  • ghostsf

    是啊。

    总体上少了 action 描述是会简洁一些,但是往往是不够的,rest 词汇和状态码都不够,也不完全支持,也达不成共识。不过无状态,对于可扩展来说,还是有必要的。

  • 完全没必要的,用着舒坦就行。不论是 restful 也好,graphql 也好,Rsocket 也好,只要写好文档,写好接口规范就行了。

    再者就是没有一种能够符合所有应用场景。比如 restful 对于多个资源以及复杂的资源操作就不是很友好。graphql 在 spring 中对于 kotlin 和 reactive 就不是那么友好。rsocket 又只支持 reactive。所以合适的场景选择合适的规范也是很重要的。

    说起来 Rsocket 不应该相提并论,但它的接口确实挺特殊(逃 ~),也逐渐成为 Spring 的一种生态。

    2 操作
    lizhongyue248 在 2020-04-25 00:52:25 更新了该回帖
    lizhongyue248 在 2020-04-25 00:52:00 更新了该回帖
请输入回帖内容 ...