RESTful API 為何成為頂流 API 架構風格?

API7_技術團隊發表於2023-02-14
作者孫毅,API7.ai 技術工程師,Apache APISIX Committer

萬物互聯的世界充滿著各式各樣的 API ,如何統籌規範 API 至關重要。RESTful API 是目前世界上最流行的 API 架構風格之一,它可以幫助你實現客戶端與服務端關注點分離,讓前後端各自迭代,提升管理效率;其無狀態的特性可以讓應用更容易擴充套件,更容易的實現快取策略從而提升系統效能和使用者體驗。本文將介紹什麼是 RESTful API 以及我們如何使用它。

首先,拋開 API 這個概念,我們來聊聊在生活中如何傳遞資訊。

當你在商店將錢拿給老闆告訴他你需要買電池,老闆在收到錢後在貨架上找到電池並遞給你。一個買電池的交易順利完成。

計算機軟體則透過 API 來完成資訊的傳遞,先來看維基百科定義:

An application programming interface (API) is a way for two or more computer programs to communicate with each other. It is a type of software interface, offering a service to other pieces of software.

軟體 A 透過 API 向軟體 B 發起請求,軟體 B 在查詢自己資源後將請求的響應內容透過 API 返回 A。

軟體 A 透過 API 向軟體 B 發起請求就好比你跟老闆說你需要電池,軟體 B 在將資料返回給軟體 A 就好比老闆找到電池後將電池給你。

這一過程軟體 B 不需要知道軟體 A 為什麼要資料,就好比商店老闆不會問你買電池的目的。軟體 A 也不需要知道 B 軟體的資料是怎麼找到的,就好比你買電池的時候你也不會問老闆電池從哪裡進貨一樣。每個軟體之間透過 API 傳遞資訊,各司其事,使得程式世界變得有序可靠。

什麼是 RESTful API

Roy Fielding 在他 2000 年的博士論文《建築風格和基於網路的軟體架構設計》中定義了REST(Representational state transfer),REST 架構風格定義了六個指導性約束。一個符合部分全部約束的系統被鬆散地稱為 RESTful。

RESTful API
(圖片來源:Seobility)

REST 的約束條件(長龍在排版時可以考慮一鍵截圖 or 分點>橫向敘述)

約束條件詳述優勢
Client–server architecture透過將使用者介面問題與資料儲存問題分開,提高了跨多個平臺的使用者介面的可移植性,並透過簡化伺服器元件提高了可擴充套件性。1. 客戶端、服務端解耦。
2. 提升使用者平臺跨平臺的可移植性。
3. 提升伺服器端的可擴充性。
Statelessness客戶端的每個請求到伺服器必須包含請求所需的所有資訊,並且不能利用伺服器上儲存的任何上下文,會話狀態完全儲存在客戶端。1. 易擴充套件,無會話依賴,任何伺服器可以處理任何請求。
2. 易快取,提升程式效能。
Cacheability要求請求響應中的資料被隱式或顯式標記為可快取或不可快取。如果一個響應是可快取的,那麼客戶端快取就被授予為以後的等效請求重用該響應資料的權利。1. 減少頻寬。
2. 減少延遲。
3. 減少伺服器負載。
4. 隱藏網路狀態。
Layered system透過約束元件行為允許架構由分層層組成,這樣每個元件都不能“看到”超出它們與之互動的直接層。透過將系統知識限制在單個層,降低了整個系統的複雜性並促進了底層獨立性。1. 降低整個系統的複雜性。
2. 促進底層的獨立性。
3. 可方便的實施負載均衡。
4. 可將業務邏輯和安全策略解耦。
Code on demand (optional)允許透過下載和執行小程式或指令碼形式的程式碼來擴充套件客戶端功能。1. 提高系統的可擴充套件性。
Uniform interface主要包含四點:
1. Resource identification in requests.
客戶能夠透過請求中包含的 URI 來識別一個資源,將服務端資源和客戶端請求資源解耦。
2. Resource manipulation through representations.
當客戶端擁有一個資源的表示,如 URI,那麼就有足夠的資訊來修改或者刪除資源。
3. Self-descriptive messages.
每個訊息都包括足夠的資訊來告知客戶客戶端該如何處理該資訊。
4. Hypermedia as the engine of application state (HATEOAS).
客戶端不需要任何額外的編碼透過服務端返回的資源連結,就可以使得使用者獲取所有的資源。
1. 簡化了整體系統架構。
2. 提高了互動的可見性。

RESTful API 最佳實踐

強調元件間的 統一介面 是 REST 架構風格與其他基於網路的風格區分開來的核心特徵,基於此特徵,本文梳理了RSETful 最佳實踐,以幫助你更好的設計 API。

路徑名稱避免動詞

使用 HTTP 方法來表達資源操作行為,而不是將行為動詞定義到路徑中。

// Good
curl -X GET http://httpbin.org/orders

// Bad
curl -X GET "http://httpbin.org/getOrders"
  • GET 獲取指定 URI 的資源資訊
// 代表獲取當前系統的所有訂單資訊
curl -X GET http://httpbin.org/orders

// 代表獲取訂單編號為 1 的訂單詳情資訊
curl -X GET http://httpbin.org/orders/1
  • POST 透過指定的 URI 建立資源
 // 代表建立一個名稱為 order 的資源
curl -X POST http://httpbin.org/orders \
  -d '{"name": "awesome", region: "A"}' \
  • PUT 建立或全量替換指定 URI 上的資源
 // 代表將 id 為 1 的 order 進行資料替換
curl -X PUT http://httpbin.org/orders/1 \
  -d '{"name": "new awesome", region: "B"}' \
  • PATCH 執行一個資源的部分更新
 // 代表將 id 為 1 的 order 中的 region 欄位進行更改,其他資料保持不變
curl -X PATCH http://httpbin.org/orders/1 \
  -d '{region: "B"}' \
  • DELETE 透過指定的 URI 移除資源
 // 代表將 id 為 1 的 order 刪除
curl -X DELETE http://httpbin.org/orders/1

URI 使用複數形式

若使用單數的形式來表示獲取某一類資源,例如:

curl -X GET "http://httpbin.org/order"

使用單數形式,會使使用者產生該系統中只有一個 order 的困惑,用複數形式在邏輯上則順暢很多。

curl -X GET "http://httpbin.org/orders"

善用 HTTP 狀態碼

HTTP 標準定義個狀態碼,大致可以分為以下幾類:

狀態碼含義
2xx成功,操作被成功接收並處理
3xx重定向,需要進一步的操作以完成請求
4xx客戶端錯誤,請求包含語法錯誤或無法完成請求
5xx伺服器錯誤,伺服器在處理請求的過程中發生了錯誤

使用標準狀態碼,開發人員可以立即識別問題,可以減少發現不同型別錯誤的時間。

版本管理

隨著業務需求的變更,已經上線的 API 大機率是要對應調整的。如果我們的 API 有第三方在使用,讓每一個客戶端根據我們 API 的變更而變更顯然是不可能的,這個時候就要引入 API 版本管理概念了,既可以保證歷史 API 正常使用,又可以迭代新的 API 以滿足新的業務需求。

常見的版本控制手段有:

  • 透過請求中路徑來做版本控制
// 請求 v1 版本的API
curl  http://httpbin.org/v1/orders

// 請求 v2 版本的API
curl  http://httpbin.org/v2/orders
  • 透過 Query 引數來做版本控制
// 請求 v1 版本的API
curl  http://httpbin.org/orders?version=v1

// 請求 v2 版本的API
curl  http://httpbin.org/v2/orders?version=v2
  • 透過 Header 來做版本控制
// 請求 v1 版本的API
curl  http://httpbin.org/orders -H "custom-version: v1"

// 請求 v2 版本的API
curl  http://httpbin.org/orders -H "custom-version: v2"

APISIX 如何助力 RESTful API

作為一個動態、實時、高效能的 API 閘道器,Apache APISIX 可以在任何 RESTful API 服務上執行,並使用外掛來新增新的服務和擴充套件其功能,這符合 RESTful 定義中的 Layered system。此外,對於一些沒有遵循 RESTful API 定義的歷史服務, APISIX 也可以幫你在不改動原有業務程式碼的情況下完成介面的轉換,使你的介面完成 Uniform interface 這一 REST 限制條件,使你的 API 可以更好的遵守 RESTful API 規範。

分層系統:支援業務邏輯和安全邏輯的分割

你可以只用關注業務邏輯的實現,介面的安全邏輯可以交給 APISIX Authentication 類外掛處理,例如 key-auth。APISIX 支援大量的 Authentication 外掛,我們以 openid-connet為例如下圖所示:

APISIX的作用

我們可以看到,使用 APISIX(API Gateway)在業務伺服器前面加一層認證邏輯,就可以起到保護上游服務的作用,讓你的業務邏輯和安全邏輯高效解耦。

Layered system:多負載均衡協議支援

APISIX 作為 API 閘道器,可以設立在客戶端和服務端之間,完成不同的負載需求。你甚至可以自定義負載均衡的邏輯。

支援的負載均衡演算法有:

  • roundrobin: Round robin balancing with weights.
  • chash: Consistent hash.
  • ewma: Pick the node with minimum latency. See EWMA Chart for more details.
  • least_conn: Picks the node with the lowest value of (active_conn + 1) / weight. Here, an active connection is a connection being used by the request and is similar to the concept in Nginx.
  • user-defined load balancer loaded via require("apisix.balancer.your_balancer")

統一介面:使歷史 API 更加 RESTful

對於已經存在很久的歷史 API,如果沒有很好的遵循 RESTful API 準則。你可以在不改造原有 API 邏輯的情況下重新透過 APISIX 來封裝新的 API 以滿足不同的業務場景。

使用 proxy-rewrite 改寫客戶端請求

在本文中上方提過我們的路徑中不要有動詞。

例如:歷史版本 API 有 /getOrder 介面,我們可以透過 proxy-rewrite 外掛來將 API 請求代理到歷史 API 上:

curl http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
    "methods": ["GET"],
    "uri": "/orders",
    "plugins": {
        "proxy-rewrite": {
            "uri": "/getOrder",
            "scheme": "http",
        }
    },
    "upstream": {
        "type": "roundrobin",
        "nodes": {
            "127.0.0.1:80": 1
        }
    }
}'

你也可以同樣使用該外掛進行 API 版本管理上的操作。

使用 response-rewrite 外掛改寫服務端響應

當我們的歷史 API 存在響應狀態碼不規範時,我們可以透過 response-rewrite 代理 response 響應從而達到修改響應狀態碼的目的。

curl http://127.0.0.1:9080/apisix/admin/routes/1  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
    "methods": ["GET"],
    "uri": "/orders",
    "plugins": {
        "response-rewrite": {
            "status_code": 201,
            "body": "{\"code\":\"ok\",\"message\":\"new json body\"}",
            "vars":[
                [ "status","==",200 ]
            ]
        }
    },
    "upstream": {
        "type": "roundrobin",
        "nodes": {
            "127.0.0.1:80": 1
        }
    }
}'

例如,這個例子表示將請求 /orders 路徑的 API 中響應為 200 的狀態的請求修改為 201。
APISIX 支援非常豐富的外掛,期待你去挖掘更多的玩法。

總結

本文詳細說明了什麼是 API,什麼是 RESTful API 以及其最佳實踐。另外還介紹瞭如何透過 APISIX 來實現業務邏輯和安全邏輯分離,如何使用 APISIX 在不改動原有業務程式碼的情況下將歷史 API 服務更加 RESTful。希望本文對你瞭解 RESTful API 有所幫助,也歡迎你來 GitHub 一起玩耍。

相關文章