Restful 是一种从数据角度思考的 api,它真正要求的是,我们应该思考业务会对数据产生什么影响,修改哪些数据。
比如用户创建订单,如果从行为角度去思考的话,我们很容易拿到一个叫做 createOrder 的接口,但是从数据角度上的,我们就变成了,针对某个特定的 user,在其聚合的 order 列表中,新增了一条数据。抽象下来便是 users/{id}/orders。它本身体现的是数据之间的关系。而 uri 和 Method 结合,便是指代的业务上唯一的行为。对于同一个接口地址,get 就是获取数据,post 就是创建数据。
但是这样还不是严格意义上的 restful,而是 http 形式的 rpc。客户端必须得完完整整地硬编码所有的信息,来匹配后端的接口。其实整个过程,就是在写一个巨大的 Switch case,根据 uri 和 method ,匹配不同的逻辑。
举个很常见的前端做权限管理的场景,前端不得不通过枚举的形式,为所有权限场景,写一个判断逻辑。从单一平台角度来看,这个并没有什么问题。如果我有多个不同平台的客户端,我就不得不重复编写类似的判断逻辑,来保证多端的一致性。这就带来一个额外的问题,如果我的权限变更了,那么我就不得不花大量精力去在各个平台更新逻辑。
另一个我们更常见的问题便是 Openapi 的接口版本问题,当我做了一个 v2 版本的接口,这个接口仅仅是提升了性能,并没有影响数据结构,但我们依旧只能等待调用 v1 版本 api 接口的客户端不再使用后,才能彻底移除 v1 版本的代码。当然现实的场景是,我们不得不和大量兼容性的代码共存。
那么真正的 restful api 是怎样的呢,我们要参考浏览器是怎么做的。浏览器作为一个客户端,整个网站的所有数据,作为服务端。浏览器并不需要知道我们网站究竟有多少页面,每个页面要加载什么资源,也不需要把所有的页面,都一次性下载下来。对于浏览器来说,它只根据 Html,去判断当前页面要什么,会产生什么结果。比如 html 中,看到 style 标签,那就去渲染 Css 文件,看到 Script 标签,那就去执行 js 文件。看到 a 标签,那是在用户点击后,延迟加载另一个页面。浏览器只会按照自己的需求,加载自己需要的资源。
从另一个角度来说,是浏览器根据 html 中的信息,增强自身的能力。css 增强样式能力,js 增强交互能力。这种按照客户端的需求去增强自己方式,被称为按需加载。如果我需要给网页增加一个谷歌分析的能力呢,那直接在 html 中,加个 script 脚本就行了。
真正的 restful 就是模仿的这种渐进式的形式,对于用户信息,我给前端提供的不仅仅是数据,还包括当前用户信息有什么能力。这些能力,存在在 Links 的对象中,前端只需要判断 link 中,有没有对应的链接即可。
看上去很容易对吧,但是 restful 的真正构建困境,一个是能否构建合理的业务模型,另一个是能在特定架构上把业务模型实现出来。这两者结合起来的思想,就是我们常说的 ddd。
首先从技术实现模型开始说起,如果我们现在让 ai 去帮我们实现 ddd,我们大概率得到的是下面的代码
public class UserController {
private OrderService: orderService
@GET('users/{id}/orders') {
return orderService.xxx
}
}这种实现方式带来的是什么问题呢,我必须知道代码中有 orderService 这个文件,我才能在 UserController 中构建 api。随着 service 文件增多,当 ai 或者开发,就会不得不查询整个代码上下文,并且理解每一个 service 后,才能接入开发。对 ai 来说,就是无用的 token 浪费,对开发来说,就是带来了极大的上下文压力。
如果我们要在实现上,映射模型的话,我们希望的是
class User {
getOrders(){
// 数据库请求
}
}public class UserController {
private user: User
@GET('users/{id}/orders') {
return user.getOrders()
}
}这里的麻烦点在于,我为了满足充血模型封装业务逻辑,却不得不在技术上污染 domain,引入具体的技术实现。我这里是把整个
另一个麻烦的点便是,如何快速且高效的建模。
什么是真正的 Restful 的 API
一个真正的 resful API 不仅仅是基于 HTTP 的远程过程调用,它是一种遵循 Rest 架构风格设计的 API。这种架构风格的本质,是提炼和总结了互联网规模架构的原则。
其核心思想是将整个系统看做一个分布式超媒体信息获取系统。这意味着信息分布在不同的服务器上,通过超媒体相互连接。这种架构中,分布式超媒体是其关键的集成策略,它促成了一种强大的客户端与服务器之间的交互模式。
什么是分布式超媒体,它为什么重要
分布式超媒体是一种机制,它允许服务器的响应不仅包含数据,还能通过链接来指导客户端下一步可以做什么。最常见的超媒体格式就是 HTML。一个网站不仅包含文本,还包含了指向其它资源(如 CSS、JavaScript 文件或其它页面)的链接(<a> 标签,<link> 标签)
这一点之所以重要,是因为它实现了渐进式服务消费(Progressive Service Consumption)。
- 定义:渐进式服务消费指的是,客户端可以根据服务器提供的超媒体链接,按需索取并选择消费哪些关联服务。服务器提供一个主要资源,并通过链接提供”增强服务”,由客户端决定使用哪些。它的一个应用渐进增强,也是开发前端应用的最佳思考方式。
- 接耦:这种方式创造了一个松耦合的系统。服务器可以迭代其功能、增加新的链接,而不会破坏现有的客户端。客户端无需硬编码所有具体的端点,而是通过服务器返回的超媒体来动态的发现它们。
HATEOAS 是什么?它与 Restful API 有何关系?
HATEOAS 即”超媒体作为应用程序状态的控制引擎”。它是 REST 原则的集中体现,被认为是最高成熟度的 Restful API。
本质上,HATEOAS 意味着客户端与应用程序的交互完全由从服务器接收到的超媒体驱动。服务器的响应包含了所有客户端可能执行的下一步操作的链接。这使得客户端变得更加通用和具有适应性,因为它不需要预先知道所有具体的 URI 结构,只需要理解超媒体格式和链接关系 (rel) 的含义即可。
例如,一个用户资源的响应可能包含一个“subscriptions”链接,借此指导客户端如何获取该用户的订阅列表,而客户端无需预先知道 /users/{id}/subscriptions 这样的 URL 结构。