什么是 HATEOAS?完整指南 + 使用超媒体构建您自己的应用程序 🔥
大多数人肯定至少接触过一次 HATEOAS 的概念,但它究竟是什么呢?
事实上,这是一个相当古老但非常有趣的话题,即使在今天,它在发展领域也没有失去其相关性。
本文将介绍基础知识,并创建我们的第一个简单应用程序来体现这一主题。
准备好了吗?那我们开始吧!
🔎 这是什么?
很多人会先给出定义,但如果没有一些理解,我们自然就先到此为止吧。让我们先来看看我们的应用程序是如何运作的。
假设我们有一个基于某个流行框架构建的典型单页应用程序(SPA)。让我们回顾一下我们是如何从中检索数据的。
我们从客户端预先定义的 API 路由中检索数据,但如果路由发生变化怎么办?如果我们在一家拥有数千名员工的大公司工作,却永远不知道哪个后端当前是最新的,那该怎么办?
或许现在我们可以给出 HATEOAS 的定义了。
HATEOAS - 超媒体作为应用程序状态的引擎。
这意味着我们将采用一条特定的现有路径,并根据响应对象描述该路径的操作。
📦 应用的架构结构
现在,让我们来看一些具体的例子,了解这个概念能为我们的SPA应用带来什么。我们以一个简单的在线服装商店为例。
我们知道产品评论是通过我们指定的 APIreviews/{id}路由获取的,但有一天,他们突然删除了这些评论,并将其集成到了一个评论平台。结果就是,例如,当用户想购买一件夹克时,他们打开评论页面,但页面出现错误,应用停止运行。
针对这种情况,我们可以创建一个不同的应用程序。这里我们不讨论 BFF 层,让我们专注于我们的主题。如果我们创建一个products/{id}路由,并从中检索所需数据呢?让我们看看它在图中会是什么样子:
我们看到这里有一个可供使用的链接列表。现在,我们可以创建一个按钮,按钮上的链接来自响应数据。点击该按钮,就能跳转到我们需要的路由。这样一来,我们实际上只需要为每个页面设置几个路由,就能构建整个应用程序。
🔧 创建应用程序
现在,我们来为我们的服装店创建一个小型应用程序。功能很简单:点击商品即可获取相关信息。我们将使用 hmpl-js,并通过 hmpl-dom 扩展来处理单个 HTML 文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Mini HATEOAS Shop</title>
<style>
body {
padding: 20px;
}
.item {
padding: 8px;
border: 1px solid #ddd;
margin: 6px 0;
cursor: pointer;
border-radius: 6px;
}
.back {
color: blue;
cursor: pointer;
margin-top: 10px;
display: inline-block;
}
pre {
background: #f6f6f6;
padding: 8px;
border-radius: 6px;
}
</style>
<script>
(function () {
const products = [
{ id: 1, name: "T-Shirt", price: 15 },
{ id: 2, name: "Hoodie", price: 40 },
{ id: 3, name: "Jeans", price: 55 },
];
const real = fetch.bind(window);
window.fetch = async (input, init) => {
const url = typeof input === "string" ? input : input?.url || "";
await new Promise((r) => setTimeout(r, 80));
if (url.endsWith("/api/products")) {
return new Response(
products
.map(
(p) => `
<div class="item" data-id="${p.id}">
<strong>${p.name}</strong> — $${p.price}
</div>`
)
.join(""),
{ status: 200, headers: { "Content-Type": "text/html" } }
);
}
if (/\/api\/product\/\d+$/.test(url)) {
const id = +url.split("/").pop();
const p = products.find((x) => x.id === id);
return new Response(
`
<h2>${p.name}</h2>
<p>Price: $${p.price}</p>
<div class="back" data-back="/api/products">← Back</div>
<pre data-hateoas>${JSON.stringify(
{
self: `/api/product/${p.id}`,
add_to_cart: `/api/cart/add/${p.id}`,
},
null,
2
)}</pre>
`,
{ status: 200, headers: { "Content-Type": "text/html" } }
);
}
return real(input, init);
};
})();
</script>
</head>
<body>
<main>
<template data-hmpl>
<div>
<h1>Clothing Shop — HATEOAS</h1>
<div id="list">
{{#request src="/api/products"}}
{{#indicator trigger="pending"}}Loading…{{/indicator}}
{{/request}}
</div>
<div id="detail"></div>
<h3>Info:</h3>
<pre id="info">none</pre>
</div>
</template>
</main>
<script src="https://unpkg.com/json5/dist/index.min.js"></script>
<script src="https://unpkg.com/dompurify/dist/purify.min.js"></script>
<script src="https://unpkg.com/hmpl-js/dist/hmpl.min.js"></script>
<script src="https://unpkg.com/hmpl-dom/dist/hmpl-dom.min.js"></script>
<script>
document.addEventListener("click", async (e) => {
const item = e.target.closest("[data-id]");
if (item) {
const id = item.dataset.id;
const html = await (await fetch("/api/product/" + id)).text();
detail.innerHTML = html;
info.textContent =
detail.querySelector("[data-hateoas]")?.textContent || "none";
}
const back = e.target.closest("[data-back]");
if (back) {
const html = await (await fetch(back.dataset.back)).text();
list.innerHTML = html;
detail.innerHTML = "";
}
});
</script>
</body>
</html>
因此,我们的应用程序只有大约 120 行代码,代码如下所示:
如您所见,得益于hmpl-js和 HATEOAS 架构,我们只需几十行代码即可创建酷炫且功能强大的应用程序,而且由于我们没有设置严格的路由,因此该应用程序可以非常灵活地与模拟服务器一起使用。
✅ 结论
本文探讨了HATEOAS架构,并使用该架构创建了一个小型应用程序。事实上,HATEOAS架构在应用程序开发领域并未受到太多关注,这实属不妥,因为其灵活性使我们能够创建更具可扩展性和易于维护的网站。
非常感谢您阅读这篇文章❤️!
你觉得这种建筑风格怎么样?你是否在自己的项目中运用过,或者在其他项目中见过?欢迎在评论区留言!
PS:也别忘了给HMPL点个赞哦!
文章来源:https://dev.to/anthonymax/what-is-hateoas-a-complete-guide-build-your-own-app-using-hypermedia-k56


