超媒体 API 是一种返回超媒体的API,通常是通过HTTP传输HTML。这种风格的API与不返回超媒体的数据API有所区别。当今最常见的数据API形式是普遍存在的JSON API。
这两种不同类型的API具有截然不同的设计需求,因此在创建时应采用不同的设计约束和目标。
超媒体API:
另一方面,数据API:
如今,API通常被认为是JSON-over-HTTP形式。这些几乎总是面向数据的API而非超媒体API,尽管偶尔会融入超媒体概念(通常对最终用户益处不大)。随着业界开始认识到将数据API强行套入REST模型的问题,已出现脱离REST风格API的趋势。
这是好事:业界应质疑数据API领域的REST理念,并开始关注能更好服务该网络架构的传统客户端-服务器技术,而将REST留给它最初描述的网络架构:超媒体API。
为了展示超媒体API如何区别于数据API,考虑以下在htmx discord上出现的情境:
我想要一个包含表单和表格的页面。表单将向表格添加新元素,表格还将每30秒轮询一次以显示其他用户的更新。
让我们以基础URL /contacts
来思考这个UI。
首先需要一个端点来检索表单和当前联系人表格。这将位于/contacts
:
GET /contacts -> 渲染表单和联系人表格
接下来,我们需要能够创建联系人。这通过向同一URL发起POST实现:
GET /contacts -> 渲染表单和联系人表格
POST /contacts -> 创建新联系人,重定向到 GET /contacts
HTML大致如下:
<div>
<form action='/contacts' method="post">
<!-- 添加联系人的表单 -->
</form>
<table>
<!-- 联系人表格 -->
</table>
</div>
到目前为止,这是标准的Web 1.0应用,此时数据API和超媒体API的需求尚未明显分化,但值得注意的是超媒体API是自描述的,可以在不破坏超媒体应用的情况下修改(例如更改创建联系人的URL)。
现在我们需要使用htmx:轮询服务器获取表格更新。为此添加新端点/contacts/table
,仅渲染联系人表格:
GET /contacts -> 渲染表单和联系人表格
POST /contacts -> 创建新联系人,重定向到 GET /contacts
GET /contacts/table -> 渲染联系人表格
然后向表格添加轮询触发器:
<div>
<form action='/contacts' method="post">
<!-- 添加联系人的表单 -->
</form>
<table hx-trigger="every 30s" hx-get="/contacts/table" hx-swap="outerHTML">
<!-- 联系人表格 -->
</table>
</div>
这里我们看到超媒体API和数据API开始分化。这个新端点完全由超媒体需求驱动,而非数据模型需求。如果应用的超媒体需求变更,该端点可以消失;其形式可能大幅改变等,这完全可接受,因为系统是自描述的。
既然已更新HTML使用htmx轮询,不妨也让表单使用htmx以获得更好用户体验:
<div>
<form action='/contacts' method="post" hx-boost="true">
<!-- 添加联系人的表单 -->
</form>
<table hx-trigger="every 30s" hx-get="/contacts/table" hx-swap="outerHTML">
<!-- 联系人表格 -->
</table>
</div>
如果需要,可以添加其他端点用于输入验证、动态表单等。这些端点将由超媒体需求驱动,而非任何数据模型考虑:我们根据应用要实现的目标来思考。
这篇短文的关键点是:超媒体系统中的API变更没有问题,因为超媒体系统中的消息是自描述的。我们可以频繁变更API而应用不会崩溃:人类用户只需看到新的超媒体(HTML)并选择他们想要的操作。
与计算机相比,人类擅长决定该做什么且相对适应变化。
这与数据API形成对比。数据API的变更会破坏客户端代码,因此变更必须更加规范。数据API还面临提供更高表达性的压力,以便无需修改即可满足更多客户端需求。
设计超媒体API时,应采用与数据API不同的设计思维。变更的担忧要少得多,提供良好超媒体体验所需的端点应是您的主要目标。