Restful:学习抽象 web
Restful API 是对整个互联网架构的反思,它的核心源头是这么一个问题:web 互联网作为目前世界上最大的软件应用,那么是怎样的架构原则,支撑了这样的系统呢?
很多八股文面试中,都有一个”输入 URL 浏览器发生了什么”的面试题,在整个过程中,我们通过 URI 访问指定页面,我们从来不需要关注页面中的 css、javascript 资源来自哪里,关联的页面是否来同一个网站。浏览器会自动解析相关的资源。
同样的,点击页面中的资源,浏览器也是帮我们额外的一个资源,并将计算结果在浏览器客户端显示。这个可以说是,浏览器通过延迟调用的方式,整合了其他系统的服务。
不论是 css、js 这样的资源,还是超链接引用的其他页面,都是服务端返回的 html 中描述的。而客户端只要根据描述的格式,在恰当的时机(比如 html 渲染后进行 cssom 树构建,然后再执行 js 文件),访问这些关联的资源,也就是整合其它系统提供的服务。
对于关联资源的访问是由客户端决定的,并不是由服务端强制的。那么客户端就可以基于自己的需求,有选择性地访问资源。
比如在智能手机大规模流行之前,比如,在智能手机大规模流行之前,大部分手机上的浏览器不具备完整执行 js 的能力,因而很多浏览器选择不加载任何 js 资源。
从这样的角度出发,我们就会发现,CSS、JavaScript 或者页面中通过超链接引用的其它页面,都可以看做是对当前页面的增强。CSS 增强了样式,JS 增强了交互,超链接提供了额外信息。
这种基于自己的需求,按需索取,不要求消费全部关联的服务。被称为渐进式消方式。促使互联网成为了一个异构、松耦合且开放的系统。
对于我们的的后端接口,我们也希望完全可以不用考虑前端是以何种形式使用,不论是 pc、h5、app,还是未来可能的通过 mcp 直接调用。那么参考分布式超媒体的形式,自然成为了我们的最佳选择。
对于超文本媒体格式,它的主要构成有两个:
- 指向关联资源的链接(href);
- 与主资源是哪种关联关系(rel);
HAL (Hypertext Application Language)JSON 就是这样一个格式
{
"_links": {
"self" : "/users"
"next" : "/users?page=4"
},
"_embedded": {
"users": [
{
"_links": {
"self": "/users/18"
},
"username" : "用户 18",
...
},
{
"_links": {
"self": "/users/19"
},
"username" : "用户 19",
...
},
...
]
},
"total": 43215,
...
}
比如这个部分中,user 对象也许有很多属性(比如年龄、地址),如果我们在集合接口中,把所有内容都返回回来,那么整个 json 就会不可避免的膨胀。如果不包含全部信息,客户端可能就无法拿到一些特定的数据。
而集合列表接口 /users
和每个 User 对象的 self uri
,就可以看做渐进式的两种不同服务。
对于集合资源中包含的数据,看作是更基础的服务,可以满足大部分客户端在通常情况下对用户数据的基础数据需求。而其中每一个 User 对象包含的 Self url 请求到的,我们则看做是增强服务,也就是全量数据。
这样我们就可以通过超媒体的形式,去支撑不同的客户端。虽然看上去有性能问题,但是我们完全可以将 self 作为主键,用作缓存来解决这个问题。不如说,只要是互联网上 Get 的资源,全部都是通过缓存的形式来维持大量客户端调用的压力。
DDD:restful 的基石
我们设计 api 作为对外暴露的接口,一定是希望其高度稳定。只要业务不变、接口就永远保持一致。而这,就和领域驱动设计中,尝试寻找一个业务上稳固的不变点,借此应对软件中的不确定性不谋而合。
什么是稳固的不变点,什么又是不确定性呢。以我们最常见的商城应用为例,在整个软件生命周期中,用户下单、付款、通知发货这几个行为,几乎是不可能发生变化的。而使用什么方式付款(微信 or 支付宝)、如何通知发货(邮件 or 短信)、如何发货(什么快递公司)。则是完全可以随着软件生命周期变动,也许一开始只能支持微信支付、未来出海还会支持苹果支付,paypal 等形式。