1. 概述
Web API 隨着 REST (表意層傳輸) 的興起而迅速發展。基於 REST 的 API 允許開發者構建模塊化、標量化且鬆散耦合的強大 Web 應用程序。雖然 RESTful API 提供了一個堅實的基礎,但它們通常缺乏可發現性和可用性的基本要素。
正是在這裏,HATEOAS (狀態為引擎的超媒體) 和 HAL (超文本應用程序語言) 可以產生重大影響。 在沒有 HATEOAS 的情況下,RESTful API 會與服務器緊密耦合,需要在客户端側硬編碼端點。
在本教程中,我們將探索 HATEOAS 和 HAL 的概念、它們之間的關係以及它們的主要區別。
2. 理解 HATEOAS
HATEOAS 代表 Hypermedia 作為應用程序狀態的引擎。 為了更深入地理解這個概念,我們首先需要理解 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"
}
]
}
在上面的示例中,客户端可以添加或刪除項目、繼續結賬或清除購物車。 所有這些操作都基於購物車中可用的項目。 links 鍵表示可用的操作。 現在,讓我們考慮一個空購物車的例子:
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 允許在資源表示中包含超媒體鏈接。 _links 屬性列出與資源相關的鏈接。 每個鏈接包含一個 rel(關係類型)和一個 href,客户端可以使用它來與 API 交互。 _links 屬性還包含一個自鏈接,允許客户端直接訪問當前資源。
3.2. 嵌入式資源
正如其名稱所示,嵌入式資源表示其他資源包含在給定的 REST 資源中。 _embedded 屬性可以包含相關的資源。 這允許客户端在不發出任何額外請求的情況下訪問相關信息。
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 是一個具體的格式,通過標準化如何表示資源及其連接 via 鏈接來幫助實現它。
讓我們以早先討論的購物車的例子為例。 當客户端請求有關包含項目的購物車的詳細信息時,服務器會返回一個 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 在一段時間內獨立演進。