如何設計Web API?

大多數現代的Web應用程序都公開了客戶端可以用來與應用程序交互的API。精心設計的Web API應該旨在支持:

  • 平臺獨立性。無論內部如何實現API,任何客戶端都應該能夠調用API。這需要使用標準協議,並具有一種機制,使客戶端和Web服務可以就要交換的數據格式達成一致。

  • 服務演進。Web API應該能夠獨立於客戶端應用程序進行演化和添加功能。隨着API的發展,現有的客戶端應用程序應繼續運行而無需修改。所有功能都應該是可發現的,以便客戶端應用程序可以充分使用它。

本文介紹了設計Web API時應考慮的問題。

REST簡介

在2000年,羅伊·菲爾丁(Roy Fielding)提出了代表性狀態轉移(REST)作爲設計Web服務的體系結構方法。REST是一種用於構建基於超媒體的分佈式系統的體系結構樣式。REST獨立於任何底層協議,不一定與HTTP綁定。但是,大多數常見的REST實現使用HTTP作爲應用程序協議,並且本指南重點介紹爲HTTP設計REST API。

REST相對於HTTP的主要優勢在於它使用開放標準,並且不會將API或客戶端應用程序的實現綁定到任何特定的實現。例如,REST Web服務可以用ASP.NET編寫,並且客戶端應用程序可以使用可以生成HTTP請求並解析HTTP響應的任何語言或工具集。

以下是使用HTTP的RESTful API的一些主要設計原則:

  • REST API是圍繞資源設計的,資源是客戶端可以訪問的任何類型的對象,數據或服務。

  • 資源具有標識符,它是唯一標識該資源的URI。例如,特定客戶訂單的URI可能是: https://www.abc.com/orders/1

  • 客戶端通過交換資源表示與服務交互。許多Web API使用JSON作爲交換格式。例如,對上面列出的URI的GET請求可能返回此響應主體(json):
    {"orderId":1,"orderValue":99.90,"productId":1,"quantity":1}

  • REST API使用統一的接口,這有助於使客戶端和服務實現脫鉤。對於基於HTTP構建的REST API,統一接口包括使用標準HTTP動詞對資源執行操作。最常見的操作是GET,POST,PUT,PATCH和DELETE。

  • REST API使用無狀態請求模型。HTTP請求應該是獨立的,並且可以以任何順序發生,因此在請求之間保留瞬態信息是不可行的。信息存儲的唯一位置是資源本身,每個請求都應該是原子操作。此約束使Web服務具有高度可伸縮性,因爲不需要在客戶端和特定服務器之間保留任何親緣關係。任何服務器都可以處理來自任何客戶端的任何請求。也就是說,其他因素可能會限制可伸縮性。例如,許多Web服務寫入後端數據存儲,這可能很難擴展。

  • REST API由表示形式中包含的超媒體鏈接驅動。例如,以下顯示了訂單的JSON表示形式。它包含獲取或更新與訂單關聯的客戶的鏈接。

        {   

                "orderID":3,    

                "productID":2,    

                "quantity":4, 

                "orderValue":16.60, 

               "links": [

                        {"rel":"product","href":"https://adventure-works.com/customers/3", "action":"GET" },

                        {"rel":"product","href":"https://adventure-works.com/customers/3", "action":"PUT" }

                ]

        }      

2008年,倫納德·理查森(Leonard Richardson)爲Web API 提出了以下成熟度模型: 

  • 級別0:定義一個URI,並且所有操作都是對此URI的POST請求。

  • 級別1:爲單個資源創建單獨的URI。

  • 級別2:使用HTTP方法定義對資源的操作。

  • 級別3:使用超媒體(HATEOAS,如下所述)。

根據Fielding的定義,級別3對應於真正的RESTful API。實際上,許多已發佈的Web API都屬於2級左右。

圍繞資源組織API

着重於Web API公開的業務實體。例如,在電子商務系統中,主要實體可能是客戶和訂單。創建訂單可以通過發送包含訂單信息的HTTP POST請求來實現。HTTP響應指示訂單是否成功下達。如果可能,資源URI應基於名詞(資源)而不是動詞(對資源的操作)。

  •  
  •  
https://www.abc.com/orders // Goodhttps://www.abc.com/create-order // Avoid

資源不必基於單個物理數據項。例如,訂單資源可能在內部實現爲關係數據庫中的多個表,但作爲單個實體呈現給客戶。避免創建僅反映數據庫內部結構的API。REST的目的是爲實體以及應用程序可以在這些實體上執行的操作建模。客戶不應接觸內部實現。

實體通常被組合到集合中(訂單,客戶)。集合是與集合中項目無關的資源,並且應具有自己的URI。例如,以下URI可能代表訂單的集合:

    https://www.abc.com/orders

向集合URI發送HTTP GET請求可檢索集合中的項目列表。集合中的每個項目都有自己的唯一URI。對商品URI的HTTP GET請求返回該商品的詳細信息。

在URI中採用一致的命名約定。通常,對引用集合的URI使用複數名詞會有所幫助。將集合和項目的URI組織到層次結構中是一個好習慣。例如,/customers是通往客戶集合/customers/5的路徑,是通往ID等於5的顧客的路徑。這種方法有助於保持Web API的直觀性。另外,許多Web API框架都可以基於參數化URI路徑來路由請求,因此您可以爲path定義路由/customers/{id}

還請考慮不同類型的資源之間的關係以及如何暴露這些關聯。例如,/customers/5/orders可能代表客戶5的所有訂單。您也可以朝另一個方向前進,並使用URI之類的URI表示從訂單到客戶的關聯/orders/99/customer。但是,將模型擴展得太遠可能難以實施。更好的解決方案是在HTTP響應消息的正文中提供指向關聯資源的可導航鏈接。

在更復雜的系統中,提供URI使客戶端能夠瀏覽多個級別的關係(例如)可能很誘人/customers/1/orders/99/products。但是,如果將來資源之間的關係發生變化,則這種複雜性級別可能難以維護,並且很難保持靈活性。相反,請嘗試使URI保持相對簡單。一旦應用程序引用了資源,就應該可以使用該引用來查找與該資源有關的項目。可以將前面的查詢替換爲URI,/customers/1/orders以查找客戶1的所有訂單,然後/orders/99/products查找此訂單中的產品。

另一個因素是,所有Web請求都會對Web服務器施加負載。請求越多,負載越大。因此,請嘗試避免使用暴露大量小資源的“聊天” Web API。這樣的API可能要求客戶端應用程序發送多個請求以查找其所需的所有數據。取而代之的是,您可能想對數據進行非規範化並將相關信息合併爲更大的資源,這些資源可以通過單個請求檢索。但是,您需要在此方法與獲取客戶端不需要的數據的開銷之間取得平衡。檢索大對象可能會增加請求的延遲,並導致額外的帶寬成本。

避免在Web API和基礎數據源之間引入依賴關係。例如,如果您的數據存儲在關係數據庫中,則Web API不需要將每個表都顯示爲資源的集合。實際上,這可能是一個糟糕的設計。相反,可以將Web API視爲數據庫的抽象。如有必要,請在數據庫和Web API之間引入一個映射層。這樣,客戶端應用程序就不會與基礎數據庫方案的更改保持隔離。

最後,可能無法將Web API實施的每個操作映射到特定資源。您可以通過調用功能的HTTP請求來處理此類非資源方案,並將結果作爲HTTP響應消息返回。例如,實現簡單的計算器操作(例如加法和減法)的Web API可以提供將這些操作公開爲僞資源的URI,並使用查詢字符串來指定所需的參數。例如,對URI / add?operand1 = 99&operand2 = 1的GET請求將返回帶有包含值100的正文的響應消息。但是,僅保留使用這些形式的URI。

根據HTTP方法定義操作

HTTP協議定義了許多將語義分配給請求的方法。大多數RESTful Web API使用的常見HTTP方法是:

  • GET以指定的URI檢索資源的表示形式。響應消息的正文包含所請求資源的詳細信息。

  • POST在指定的URI處創建一個新資源。請求消息的正文提供了新資源的詳細信息。請注意,POST也可以用於觸發實際上並不創建資源的操作。

  • PUT可以創建或替換指定URI處的資源。請求消息的正文指定要創建或更新的資源。

  • PATCH執行資源的部分更新。請求主體指定要應用於資源的一組更改。

  • DELETE刪除指定URI處的資源。

特定請求的效果應取決於資源是集合還是單個項目。下表使用電子商務示例總結了大多數RESTful實現所採用的通用約定。並非所有這些請求都可以實現-這取決於特定的方案。

資源資源 開機自檢 得到 刪除
/顧客 建立新客戶 檢索所有客戶 批量更新客戶

刪除所有

客戶

/客戶/ 1 錯誤 檢索客戶1的詳細信息 更新客戶1的詳細信息(如果存在)

刪除客

戶1

/ customers / 1 /訂單 爲客戶1創建新訂單 檢索客戶1的所有訂單 批量更新客戶1的訂單 刪除客戶1的所有訂單

POST,PUT和PATCH之間的差異可能令人困惑。

  • POST請求創建資源。服務器爲新資源分配一個URI,並將該URI返回給客戶端。在REST模型中,您經常將POST請求應用於集合。新資源將添加到集合中。POST請求也可以用於提交數據以處理現有資源,而無需創建任何新資源。

  • PUT請求創建資源更新現有資源。客戶端指定資源的URI。請求主體包含資源的完整表示。如果具有此URI的資源已存在,則將其替換。否則,如果服務器支持,則創建一個新資源。PUT請求最常應用於單個項目(例如特定客戶)而不是集合的資源。服務器可能支持更新,但不支持通過PUT創建。是否支持通過PUT創建取決於客戶端是否可以在資源存在之前爲資源有意義地分配URI。如果不是,則使用POST創建資源,並使用PUT或PATCH更新。

  • PATCH請求對現有資源執行部分更新。客戶端指定資源的URI。請求主體指定一組更改以應用於資源。這可能比使用PUT更有效,因爲客戶端僅發送更改,而不發送資源的完整表示。從技術上講,如果服務器支持,PATCH也可以創建新資源(通過爲“空”資源指定一組更新)。

PUT請求必須是冪等的。如果客戶端多次提交相同的PUT請求,則結果應始終相同(將使用相同的值修改相同的資源)。POST和PATCH請求不保證是冪等的。

符合HTTP語義

本節描述了設計符合HTTP規範的API的一些典型注意事項。但是,它沒有涵蓋所有可能的細節或場景。如有疑問,請查閱HTTP規範。

媒體類型

如前所述,客戶端和服務器交換資源的表示形式。例如,在POST請求中,請求主體包含要創建的資源的表示形式。在GET請求中,響應主體包含獲取的資源的表示形式。

在HTTP協議中,通過使用媒體類型(也稱爲MIME類型)來指定格式。對於非二進制數據,大多數Web API支持JSON(媒體類型= application / xml),可能還支持XML(媒體類型= application / xml)。

請求或響應中的Content-Type標頭指定表示的格式。這是一個包含JSON數據的POST請求示例:

    POSThttps://adventure-works.com/orders HTTP/1.1

    Content-Type: application/json; charset=utf-8

    Content-Length: 57

    {"Id":1,"Name":"Gizmo","Category":"Widgets","Price":1.99}

如果服務器不支持媒體類型,則應返回HTTP狀態碼415(不支持的媒體類型)。

客戶端請求可以包含一個Accept標頭,該標頭包含客戶端將在響應消息中從服務器接受的媒體類型列表。例如:

        GEThttps://adventure-works.com/orders/2 HTTP/1.1    Accept: application/json

如果服務器無法匹配列出的任何媒體類型,則服務器應返回HTTP狀態代碼406(不可接受)。

GET方法

成功的GET方法通常返回HTTP狀態代碼200(確定)。如果找不到資源,則該方法應返回404(未找到)。

POST方法

如果POST方法創建新資源,它將返回HTTP狀態代碼201(已創建)。新資源的URI包含在響應的Location標頭中。響應主體包含資源的表示形式。

如果該方法進行了一些處理但未創建新資源,則該方法可以返回HTTP狀態代碼200,並將操作結果包括在響應主體中。或者,如果沒有要返回的結果,則該方法可以返回沒有響應正文的HTTP狀態代碼204(無內容)。

如果客戶端將無效數據放入請求中,則服務器應返回HTTP狀態代碼400(錯誤請求)。響應主體可以包含有關錯誤的其他信息,也可以包含提供更多詳細信息的URI鏈接。

 

PUT方法

如果PUT方法創建新資源,則與POST方法一樣,它返回HTTP狀態代碼201(已創建)。如果該方法更新了現有資源,則返回200(確定)或204(無內容)。在某些情況下,可能無法更新現有資源。在這種情況下,請考慮返回HTTP狀態代碼409(衝突)。

 

考慮實現批量HTTP PUT操作,該操作可以批量更新集合中的多個資源。PUT請求應指定集合的URI,請求主體應指定要修改的資源的詳細信息。這種方法可以幫助減少聊天情況並提高性能。

Delete方法

如果刪除操作成功,則Web服務器應使用HTTP狀態代碼204進行響應,指示該過程已成功處理,但是響應主體不包含其他信息。如果資源不存在,則Web服務器可以返回HTTP 404(未找到)。

異步操作

有時POST,PUT,PATCH或DELETE操作可能需要一些處理才能完成。如果在發送響應到客戶端之前等待完成,則可能會導致無法接受的延遲。如果是這樣,請考慮使操作異步。返回HTTP狀態碼202(已接受)以指示請求已接受處理,但未完成。

您應該公開一個返回異步請求狀態的端點,以便客戶端可以通過輪詢狀態端點來監視狀態。在202響應的Location標頭中包含狀態終結點的URI。例如:

    HTTP/1.1 202 Accepted

    Location: /api/status/12345

如果客戶端將GET請求發送到此端點,則響應應包含請求的當前狀態。可選地,它還可以包括估計的完成時間或取消操作的鏈接。

    HTTP/1.1 200 OK

    Content-Type: application/json

    {    

        "status":"In progress",    "link": { "rel":"cancel", "method":"delete", "href":"/api/status/12345" }    }

如果異步操作創建了新資源,則該操作完成後,狀態端點應返回狀態代碼303。在303響

應中,包括一個Location標頭,該標頭提供了新資源的URI:

      HTTP/1.1 303 See Other

    Location: /api/orders/12345

篩選和分頁數據

當僅需要一部分信息時,通過單個URI公開資源集合可能導致應用程序獲取大量數據。例如,假設客戶端應用程序需要查找成本超過特定值的所有訂單。它可能會從/ orders URI中檢索所有訂單,然後在客戶端過濾這些訂單。顯然,此過程效率很低。它浪費了網絡帶寬和託管Web API的服務器上的處理能力。

相反,API可以允許在URI的查詢字符串中傳遞過濾器,例如/ orders?minCost = n。然後,Web API負責解析和處理minCost查詢字符串中的參數,並在服務器端返回過濾後的結果。

收集資源上的GET請求可能會返回大量項目。您應該設計一個Web API來限制任何單個請求返回的數據量。考慮支持查詢字符串,這些字符串指定要檢索的最大項目數以及集合中的起始偏移量。例如:

        /orders?limit=25&offset=50

還應考慮對返回的項目數施加上限,以幫助防止拒絕服務攻擊。爲了幫助客戶端應用程序,返回分頁數據的GET請求還應該包括某種形式的元數據,以指示集合中可用資源的總數。

通過提供將字段名作爲值的排序參數,例如/ orders?sort = ProductID,可以使用類似的策略對數據進行排序。但是,此方法可能會對緩存產生負面影響,因爲查詢字符串參數形成了資源標識符的一部分,該標識符被許多緩存實現用作緩存數據的鍵。

如果每個項目包含大量數據,則可以擴展此方法以限制爲每個項目返回的字段。例如,您可以使用查詢字符串參數,該參數接受以逗號分隔的字段列表,例如/ orders?fields = ProductID,Quantity

在查詢字符串中爲所有可選參數提供有意義的默認值。例如,如果實現分頁,則將參數設置limit爲10,將offset參數設置爲0,如果實現排序,則將sort參數設置爲資源的鍵,fields如果支持投影,則將參數設置爲資源中的所有字段。

支持對大型二進制資源的部分響應

資源可能包含較大的二進制字段,例如文件或圖像。爲了克服由不可靠和間歇性連接引起的問題並改善響應時間,請考慮使此類資源能夠分塊檢索。爲此,Web API應該支持用於大資源的GET請求的Accept-Ranges標頭。此標頭表示GET操作支持部分請求。客戶端應用程序可以提交GET請求,該請求返回指定爲字節範圍的資源子集。

另外,請考慮爲這些資源實現HTTP HEAD請求。HEAD請求與GET請求相似,不同之處在於,它僅返回描述資源的HTTP標頭,且消息正文爲空。客戶端應用程序可以發出HEAD請求,以確定是否通過使用部分GET請求來獲取資源。例如:

    HEADhttps://adventure-works.com/products/10?fields=productImage HTTP/1.1

這是示例響應消息:

        HTTP/1.1 200 OK

        Accept-Ranges: bytes

        Content-Type: image/jpeg

        Content-Length: 4580

 

Content-Length標頭提供資源的總大小,Accept-Ranges標頭指示相應的GET操作支持部分結果。客戶端應用程序可以使用此信息以較小的塊來檢索圖像。第一個請求通過使用Range標頭獲取前2500個字節:

        HTTP/1.1 303 See Other

        Location: /api/orders/12345

篩選和分頁數據

當僅需要一部分信息時,通過單個URI公開資源集合可能導致應用程序獲取大量數據。例如,假設客戶端應用程序需要查找成本超過特定值的所有訂單。它可能會從/ orders URI中檢索所有訂單,然後在客戶端過濾這些訂單。顯然,此過程效率很低。它浪費了網絡帶寬和託管Web API的服務器上的處理能力。

相反,API可以允許在URI的查詢字符串中傳遞過濾器,例如/ orders?minCost = n。然後,Web API負責解析和處理minCost查詢字符串中的參數,並在服務器端返回過濾後的結果。

收集資源上的GET請求可能會返回大量項目。您應該設計一個Web API來限制任何單個請求返回的數據量。考慮支持查詢字符串,這些字符串指定要檢索的最大項目數以及集合中的起始偏移量。例如:

    /orders?limit=25&offset=50

還應考慮對返回的項目數施加上限,以幫助防止拒絕服務攻擊。爲了幫助客戶端應用程序,返回分頁數據的GET請求還應該包括某種形式的元數據,以指示集合中可用資源的總數。

通過提供將字段名作爲值的排序參數,例如/ orders?sort = ProductID,可以使用類似的策略對數據進行排序。但是,此方法可能會對緩存產生負面影響,因爲查詢字符串參數形成了資源標識符的一部分,該標識符被許多緩存實現用作緩存數據的鍵。

如果每個項目包含大量數據,則可以擴展此方法以限制爲每個項目返回的字段。例如,您可以使用查詢字符串參數,該參數接受以逗號分隔的字段列表,例如/ orders?fields = ProductID,Quantity

在查詢字符串中爲所有可選參數提供有意義的默認值。例如,如果實現分頁,則將參數設置limit爲10,將offset參數設置爲0,如果實現排序,則將sort參數設置爲資源的鍵,fields如果支持投影,則將參數設置爲資源中的所有字段。

支持對大型二進制資源的部分響應

資源可能包含較大的二進制字段,例如文件或圖像。爲了克服由不可靠和間歇性連接引起的問題並改善響應時間,請考慮使此類資源能夠分塊檢索。爲此,Web API應該支持用於大資源的GET請求的Accept-Ranges標頭。此標頭表示GET操作支持部分請求。客戶端應用程序可以提交GET請求,該請求返回指定爲字節範圍的資源子集。

另外,請考慮爲這些資源實現HTTP HEAD請求。HEAD請求與GET請求相似,不同之處在於,它僅返回描述資源的HTTP標頭,且消息正文爲空。客戶端應用程序可以發出HEAD請求,以確定是否通過使用部分GET請求來獲取資源。例如:

    HEADhttps://adventure-works.com/products/10?fields=productImage HTTP/1.1
這是示例響應消息:

        HTTP/1.1 200 OK

        

        Accept-Ranges: bytes

        Content-Type: image/jpeg

        Content-Length: 4580

 

Content-Length標頭提供資源的總大小,Accept-Ranges標頭指示相應的GET操作支持部分結果。客戶端應用程序可以使用此信息以較小的塊來檢索圖像。第一個請求通過使用Range標頭獲取前2500個字節:

    GEThttps://adventure-works.com/products/10?fields=productImage HTTP/1.1

    Range: bytes=0-2499

響應消息通過返回HTTP狀態代碼206指示這是部分響應。Content-Length標頭指定了消息正文中返回的實際字節數(不是資源的大小),而Content-Range標頭指示了哪個響應。這是資源的一部分(4580中的字節0-2499):

        HTTP/1.1 206 Partial Content

       

        Accept-Ranges: bytes

        Content-Type: image/jpeg

        Content-Length: 2500

        Content-Range: bytes 0-2499/4580

來自客戶端應用程序的後續請求可以檢索資源的其餘部分。

使用HATEOAS啓用對相關資源的導航

REST背後的主要動機之一是,無需事先了解URI方案,就應該可以瀏覽整個資源集。每個HTTP GET請求應通過響應中包含的超鏈接返回查找與請求的對象直接相關的資源所必需的信息,並且還應向其提供描述這些資源中的每一個可用操作的信息。此原理稱爲HATEOAS,或稱爲應用程序狀態引擎的超文本。該系統實際上是一個有限狀態機,對每個請求的響應都包含從一種狀態轉移到另一種狀態所需的信息。不需要其他信息。

對RESTful Web API進行版本控制

Web API保持靜態的可能性很小。隨着業務需求的變化,可能會添加新的資源集合,資源之間的關係可能會更改,並且資源中的數據結構可能會被修改。儘管更新Web API以處理新的或不同的要求是一個相對簡單的過程,但是您必須考慮此類更改將對使用Web API的客戶端應用程序產生的影響。問題在於,儘管設計和實現Web API的開發人員可以完全控制該API,但是開發人員對客戶端應用程序的控制程度不同,該客戶端應用程序可以由遠程運行的第三方組織構建。

通過版本控制,Web API可以指示其公開的功能和資源,並且客戶端應用程序可以提交針對功能或資源的特定版本的請求。以下各節描述了幾種不同的方法,每種方法都有其自身的優點和取捨。

沒有版本控制

這是最簡單的方法,對於某些內部API可能是可接受的。重大更改可以表示爲新資源或新鏈接。向現有資源添加內容可能不會帶來重大變化,因爲不希望看到此內容的客戶端應用程序將忽略它。

例如,到URI的請求https://www.abc.com/customers/3應該返回單個客戶的含有細節idname以及address由所述客戶端應用程序預期字段:

    HTTP/1.1 200 OK

    Content-Type: application/json; charset=utf-8                    {"id":3,"name":"Contoso LLC","address":"1 Microsoft Way Redmond WA 98053"}

如果將該DateCreated字段添加到客戶資源的架構,則響應將如下所示:

        HTTP/1.1 200 OK

        Content-Type: application/json; charset=utf-8

        {"id":3,"name":"Contoso LLC","dateCreated":"2014-09-04T12:11:38.0376089Z","address":"1 Microsoft Way Redmond WA 98053"}

如果現有的客戶端應用程序能夠忽略無法識別的字段,則它們可能會繼續正常運行,而新的客戶端應用程序可以設計爲處理此新字段。但是,如果對資源的架構進行了更根本的更改(例如,刪除或重命名字段),或者資源之間的關係發生了更改,則這些更改可能構成重大更改,從而阻止現有客戶端應用程序正常運行。在這些情況下,您應該考慮使用以下方法之一。

URI版本控制

每次您修改Web API或更改資源架構時,您都會爲每個資源的URI添加一個版本號。先前存在的URI應該繼續像以前一樣操作,返回符合其原始架構的資源。

延伸的前面的例子,如果該address字段被重組爲包含地址的每個組成部分(如子場streetAddresscitystate,和zipCode),這個版本的資源的可通過URI暴露包含一個版本號,如 https://adventure-works.com/v2/customers/3

        HTTP/1.1 200 OK

        Content-Type: application/json; charset=utf-8

     {"id":3,"name":"Contoso LLC","dateCreated":"2014-09-04T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}

這種版本控制機制非常簡單,但是取決於服務器將請求路由到適當的端點。但是,隨着Web API通過多次迭代成熟,並且服務器必須支持許多不同的版本,它可能變得笨拙。同樣,從純粹主義者的角度來看,在所有情況下,客戶端應用程序都在提取相同的數據(客戶3),因此URI不應真正取決於版本。此方案還使HATEOAS的實現複雜化,因爲所有鏈接都需要在其URI中包括版本號。

查詢字符串版本控制

您可以使用附加到HTTP請求的查詢字符串中的參數(例如)來指定資源的版本,而不是提供多個URI https://www.abc.com/customers/3?version=2。如果較舊的客戶端應用程序省略了版本參數,則該版本參數應默認爲有意義的值,例如1。

這種方法具有語義優勢,即始終從相同的URI中檢索相同的資源,但是它取決於處理請求的代碼以解析查詢字符串併發送回適當的HTTP響應。這種方法還遭受與URI版本控制機制相同的實現HATEOAS的複雜性。

標頭版本控制

您可以實現一個指示資源版本的自定義標頭,而不是將版本號附加爲查詢字符串參數。這種方法要求客戶端應用程序將適當的標頭添加到任何請求,儘管如果省略了版本標頭,則處理客戶端請求的代碼可以使用默認值(版本1)。以下示例使用名爲Custom-Header的自定義標。此標頭的值指示Web API的版本。

版本1:

        GEThttps://www.abc.com/customers/3 HTTP/1.1

        Custom-Header: api-version=1

     HTTP/1.1 200 OK

     Content-Type: application/json; charset=utf-8

     {"id":3,"name":"Contoso LLC","address":"1 Microsoft Way Redmond WA 98053"}

版本2:

    GEThttps://www.abc.com/customers/3 HTTP/1.1

    Custom-Header: api-version=2

    HTTP/1.1 200 OK

    Content-Type: application/json; charset=utf-8

    {"id":3,"name":"Contoso LLC","dateCreated":"2014-09-04T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}

與前兩種方法一樣,實現HATEOAS要求在任何鏈接中包括適當的自定義標頭。

媒體類型版本控制

當客戶端應用程序將HTTP GET請求發送到Web服務器時,應按照本指南前面所述,指定它可以使用Accept標頭處理的內容格式。通常,Accept標頭的目的是允許客戶端應用程序指定響應的主體是XML,JSON還是客戶端可以解析的其他常見格式。但是,可以定義包括信息的自定義媒體類型,該信息使客戶端應用程序能夠指示期望的資源版本。以下示例顯示了一個請求,該請求使用值application / vnd.adventure-works.v1 + json指定一個Accept報頭。該vnd.adventure-works.v1元素向Web服務器指示應返回資源的版本1,而json元素則指定響應主體的格式應爲JSON:

    GEThttps://adventure-works.com/customers/3 HTTP/1.1

    Accept: application/vnd.adventure-works.v1+json

 

處理請求的代碼負責處理Accept報頭並儘可能地兌現它(客戶端應用程序可以在

Accept報頭中指定多種格式,在這種情況下,Web服務器可以爲響應正文選擇最合適

的格式)。Web服務器通過使用Content-Type標頭確認響應正文中的數據格式:

    HTTP/1.1 200 OK

    Content-Type: application/vnd.adventure-works.v1+json; charset=utf-8

    {"id":3,"name":"Contoso LLC","address":"1 Microsoft Way Redmond WA 98053"}

如果Accept標頭未指定任何已知的媒體類型,則Web服務器可以生成HTTP 406(不可接受)響應消息或返回具有默認媒體類型的消息。

這種方法可以說是最純粹的版本控制機制,並且很自然地適合HATEOAS,後者可以在資源鏈接中包括相關數據的MIME類型。

注意:選擇版本控制策略時,還應考慮對性能的影響,尤其是Web服務器上的緩存。URI版本控制和查詢字符串版本控制方案是緩存友好的,因爲相
同的URI /查詢字符串組合每次都引用相同的數據。標頭版本控制和媒體類型版本控制機制通常需要其他邏輯來檢查自定義標頭或“接受”標頭
中的值。在大規模環境中,許多使用不同版本的Web API的客戶端可能會在服務器端緩存中導致大量重複數據。如果客戶端應用程序通過實現
緩存的代理與Web服務器通信,並且僅在當前不將請求數據的副本保存在其緩存中的情況下將請求轉發到Web服務器,則此問題可能變得很嚴
重。

感謝閱讀!

                                                                  喜歡本文的朋友,歡迎關注“isevena

                                                   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章