1. 概述
Web API 隨着 REST (表意層傳輸) 的興起而迅速發展。基於 REST 的 API 允許開發者構建模塊化、標量化且鬆散耦合的強大 Web 應用程序。雖然 RESTful API 提供了一個堅實的基礎,但它們經常缺乏可發現性和可用性等關鍵要素。
這時,HATEOAS (狀態為引擎的超媒體) 和 HAL (超文本應用程序語言) 就能發揮重要作用。 如果沒有 HATEOAS,RESTful API 會與服務器緊密耦合,需要在客户端硬編碼端點。
在本教程中,我們將探索 HATEOAS 和 HAL 的概念、它們之間的關係以及它們的關鍵差異。
2. 理解 HATEOAS
HATEOAS 代表 Hypermedia as the Engine of Application State。要更深入地理解這個概念,首先我們需要理解 Hypermedia 的含義。 Hypermedia 是超文本的擴展,它包含文本鏈接和其他媒體類型,例如圖像、音頻和視頻。它允許通過啓用用户無縫地導航相關資源來實現豐富的交互。
HATEOAS 通過在響應中嵌入 Hypermedia 鏈接來增強 API 交互。這允許客户端在無需事先了解 API 結構的情況下,動態地導航和與資源交互。它促進鬆耦合,並允許 API 在獨立地演變。
這意味着客户端-服務器交互應該完全依賴於服務器響應中收到的 Hypermedia。每個服務器響應都包含鏈接,以指導客户端發現有關其他操作和資源的更多信息以及數據。
讓我們通過購物車的例子來更深入地瞭解 HATEOAS:
當客户端請求有關包含項目的購物車的詳細信息時,服務器會提供有關可能操作的相關鏈接:
GET /cart/12345 HTTP/1.1
HTTP/1.1 200 OK
{
"cartId": 12345,
"items": [
{
"id": 001,
"name": "TV",
"qty": 1,
"amount": 4750
}
],
"totalAmount": 4750,
"links": [
{
"rel": "self",
"href": "/cart/12345"
},
{
"rel": "addItem",
"href": "/cart/12345/add"
},
{
"rel": "removeItem",
"href": "/cart/12345/remove"
},
{
"rel": "checkout",
"href": "/cart/12345/checkout"
},
{
"rel": "clear",
"href": "/cart/12345/clear"
}
]
}在上述示例中,客户可以添加或刪除更多項目、進入結算流程或清空購物車。所有這些操作都基於購物車中可用的項目。<em links</em> 鍵指示了可用的操作。現在,讓我們考慮一個空購物車示例:
GET /cart/987 HTTP/1.1
HTTP/1.1 200 OK
{
"cartId": 987,
"items": [],
"totalAmount": 0.0,
"links": [
{
"rel": "self",
"href": "/cart/987"
},
{
"rel": "addItem",
"href": "/cart/987/add"
},
{
"rel": "checkout",
"href": "/cart/987/checkout"
},
]
}現在,客户可用的操作受到限制。客户可以向購物車添加商品或進入結算流程,但由於購物車已為空,因此他們無法清除或刪除商品。這段超文本告訴我們允許的操作以及不允許的操作。
3. 理解 HAL
HAL 是一種簡單格式,幫助開發者為 RESTful API 創建超媒體表示,支持 HATEOAS 原則。它定義了一種簡單格式,提供了一種便捷、一致的方式來在 REST API 中鏈接資源。
讓我們來了解一下 HAL 的關鍵概念。
3.1. 鏈接
HAL 允許在資源表示中包含超媒體鏈接。<em >_links </em > 屬性列出了與資源相關的鏈接。每個鏈接包含一個 <em >rel </em >(關係類型)和 <em >href </em >,客户端可以使用這些鏈接與 API 進行交互。<em >_links </em > 屬性還包含自鏈接,允許客户端直接訪問當前資源。
3.2. 嵌入式資源
如名稱所示,嵌入式資源表明其他資源包含在給定的 REST 資源中。<em >_embedded</em> 屬性可以包含相關資源。這允許客户端訪問相關信息,而無需發出任何額外的請求。
3.3. 狀態
HAL 使用 JSON 或 XML 對資源數據及其關聯鏈接進行編碼。
現在,我們來看一個不使用 HAL 的示例,API 的響應將如下所示:
{
"cartId": 12345,
"items": [
{
"id": 001,
"name": "TV",
"qty": 1,
"amount": 4750
}
]
}上述示例並未提供任何相關資源的鏈接。現在,我們將添加 HAL 鏈接到響應中,使其成為符合 HAL 規範的示例:
{
"_embedded": {
"items": [
{
"id": 001,
"name": "TV",
"qty": 1,
"amount": 4750,
"_links": {
"self": { "href": "/items/001" },
"update": { "href": "/items/001/update" }
}
}
]
},
{
"_links": {
"self": { "href": "/carts/12345" },
"addItem": { "href": "/cart/12345/item" },
"checkout": { "href": "/cart/12345/checkout" }
}
}
}在上述示例中,主要數據位於 _embedded 屬性下。這裏,主要數據包含購物車中項目的列表,每個項目都有自己的 _links 。
4. HATEOAS 與 HAL 的關係
HATEOAS 和 HAL 在 RESTful API 設計中是密切相關的概念。HATEOAS 是一種 REST 原則,它鼓勵使用超媒體鏈接,以便客户端能夠動態地探索 API。另一方面,HAL 是一種具體的格式,它通過標準化資源及其連接的表示方式(通過鏈接)來幫助實現這一目標。
讓我們以之前討論的購物車的示例為例。當客户端請求有關包含商品的購物車的詳細信息時,服務器將返回一個 HAL 表示形式:
GET /cart/12345 HTTP 1.1
HTTP/1.1 200 OK
{
"cartId": 12345,
"items": [{
"id": 001,
"name": "TV",
"qty": 1,
"amount": 4500
}],
"totalAmount": 4500,
"_links": {
"self": { "href": "/cart/12345", "rel": "self" },
"addItem": { "href": "/cart/12345/add", "rel": "addItem"},
"checkout": { "href": "/cart/12345/checkout", "rel": "checkout" },
"clear": { "href": "/cart/12345/clear" , "rel": "clearCart" }
},
"_embedded": {
"offer": {
"code": "DISCOUNT10",
"discount": 10
}
}在上述示例中,_links 部分提供基於當前資源狀態的客户端可執行的操作的超媒體鏈接。 rel 字段至關重要,因為它定義了鏈接所代表的操作(例如:self, addItem, checkout, 等)。它用於描述鏈接的含義以及客户端在 API 狀態的上下文中應該如何解釋它。
_embedded 部分包含直接嵌入到主資源中的相關資源。它允許客户端在不發出額外請求的情況下訪問相關數據,從而減少網絡調用並提高效率。
資源的當前狀態被捕獲在響應中返回的實際數據中,例如項目和購物車總金額。
5. HATEOAS 和 HAL 的關鍵區別
讓我們來看看 HATEOAS 和 HAL 的關鍵區別:
| 方面 | HATEOAS | HAL |
|---|---|---|
| 概念/格式 | HATEOAS 是一種 REST 架構原則,它指導客户端通過遵循服務器提供的超媒體鏈接來發現可用的操作。 | HAL 是一種用於以支持 HATEOAS 的方式表示資源的特定格式。 |
| 目的 | HATEOAS 的目的是使 API 具有自我解釋性和易於導航。它減少了客户端需要先了解 API 結構的需求。 | HAL 提供了一種清晰簡潔的方式來表示資源及其連接。它簡化了客户端解析和理解鏈接的過程。 |
| 實現 | 各種格式和技術,例如 JSON、XML,甚至 HAL 本身,都可以實現 HATEOAS。 | HAL 專門使用 JSON 或 XML 結構化資源表示,強調鏈接。 |
6. 結論
在本文中,我們討論了 HATEOAS 和 HAL。 雖然 HATEOAS 闡述了 RESTful API 應該如何運行的一個原則,但 HAL 提供了一個具體的實現,簡化了 API 中的超媒體控件。 使用 HAL,開發人員可以輕鬆創建符合 HATEOAS 的 API,從而簡化客户端的資源發現和交互。
通過結合 HATEOAS 和 HAL,API 變得自描述且易於發現。 這導致客户端和服務器之間實現更好的解耦,允許 API 在一段時間內獨立演進。