REST API設計優秀實踐之參數與查詢的使用

2021-01-07 51CTO

【51CTO.com快譯】眾所周知,我們設計API的目標往往是要通過我們的服務,為用戶提供一定的功能。雖然HTTP和URL資源都允許數據流進行一定程度的基本交互,但是它們在面對其他特定需求時,往往會讓您的API顯得力不從心。在此,我們以分頁為例,即:如果某個資料庫中存放著上百萬篇的文章,那麼我們很可能無法在單次響應中,將每一篇文章都發送給客戶。針對此類需求,業界提出了參數化(parametrization)的解決方法。

什麼是參數化?

通常而言,參數化是一種請求配置。在程式語言中,我們可以通過某個函數來請求對應的返回值。如果一個函數不帶任何參數,那麼我們將無法直接去影響它的返回值。

API也是如此,尤其是那些無狀態的REST API。畢竟,所有REST的交互都是無狀態的。也就是說,每個請求都會包含那些方便連接器理解該請求的所有信息,而且它們與之前的任何請求都無關。

在實際應用中,我們有許多種方法可以向HTTP請求中添加參數,其中包括:查詢字符串,POST、PUT報文,各種PATCH請求、以及標頭(header)。它們各自都有自己的用例和規則。

那麼,添加所有參數數據的最簡單方法,就是將所有內容放入報文內。在具體應用中,每個端點都會用到POST方法,而許多API也會把所有的參數放置在報文中。長此以往,傳統的API裡積累了越來越多的參數,以致於它們不再適合查詢字符串等操作。顯然,我們應當避免在設計API時此類極端情況的發生。

添加何種參數?

就REST而言,作為API查詢語言的GraphQL,為用戶提供了滿足數據查詢的運行時。那麼,我們是否應當添加那些在HTTP規範中已經標準化的標頭欄位類來作為參數(請參見:http://www.rfcreader.com/#rfc2616_line4589)呢?

在我們了解了各種默認標頭欄位後,下面我們來討論是否應該為自己的參數創建一個自定義的標頭欄位,或者將其放入URL的查詢字符串中。

何時該使用查詢字符串?

如果我們已獲悉待添加的參數不屬於默認的標頭欄位,且並不敏感,那麼我們就應當通過查看查詢字符串,以確認是否合適。在查詢字符串的時候,有個<isindex>的HTML元素,可被用於向伺服器發送一些關鍵字。而伺服器則會據此做出響應,並列出與關鍵字相匹配的頁面列表。接著,查詢字符串會被重新用於Web表單,以通過GET的請求方式,將數據發送到伺服器處。

因此,查詢字符串的主要用例便是過濾,它會著重過濾搜索和分頁這兩種特殊的情況。有關此方面的詳細討論,請參見:https://www.moesif.com/blog/technical/api-design/REST-API-Design-Filtering-Sorting-and-Pagination/?utm_source=dzone&utm_medium=paid&utm_campaign=placed%20article&utm_term=rest%20api%20design%20best%20practices%20for%20parameters%20and%20query%20string%20usage。不過,正如針對Web表單的目的所示,它可以被用於不同類型的參數。而RESTful API可以將POST或PUT請求與報文一起使用,以實現將表單數據發送給伺服器。

我們來看一個有關嵌套表示(nested representation)的參數示例。默認情況下,我們需要返回文章的普通表示形式(plain representation)。當我們將?withComments查詢字符串添加到端點時,?withComments會以普通表示形式返回某篇文章的評論,而且僅需要一個請求即可。至於此類參數是應該被放在自定義的標頭中,還是查詢字符串裡,則主要取決於開發人員的個人經驗與偏好。

根據HTTP的規範陳述(請參見:http://www.rfcreader.com/#rfc2616_line1761):標頭欄位可以被視為函數的參數。不過,將查詢字符串添加到URL中會明顯比創建客戶端標頭,要更快、更顯著。實際上,這些欄位充當了請求修飾符,其語義等同於程式語言在方法調用中的參數。

在實際應用中,您會發現:在所有端點上保持相同的參數更適合於標頭。例如:身份驗證令牌可以由每個請求所發送。而那些高度動態的參數(尤其是僅對少數幾個端點有效的參數)則應該被放在查詢字符串中。例如:每個端點的過濾器參數都會有所不同。

數組和映射參數

對於開發人員而言,他們可能會經常碰到的一個問題是:如何處理查詢字符串中的數組參數?例如,我們可能碰到需要搜索多個名稱的需求場景。而通常的解決方案是:使用方括號。如下列代碼所示:

不過,HTTP的規範指出:由IPv6 [RFC3513]或更高版本標識的主機通過將IP方括號(「[」和「]」)來區分。這是URI語法中唯一允許使用方括號字符的地方。

我們在許多HTTP伺服器和客戶端的實現場景中,都應當牢記上述規範。當然,為了簡便起見,我們可以採用「多次使用一個參數名稱」的另一種解決方案。如下列代碼所示:

該方法雖然有效,但是它會導致開發人員體驗上的下降。通常情況下,客戶端只會使用類似於地圖(map-like)的數據結構。而該結構在被添加到URL中之前,會進行簡單的字符串轉換。而這恰恰會導致後續的數值被覆蓋。可見,在發送請求之前,我們需要進行更加複雜的轉換。

與此同時,也有人會採用另一種方法:使用「,」字符來分隔數值,直接在URL中出現未經編碼的字符。如下列代碼所示:

而對於那些類似地圖的數據結構,我們也可以使用無需編碼的「.」字符。如下列代碼所示:

另外,您還可以對整個查詢字符串進行URL編碼(請參見:https://en.wikipedia.org/wiki/Percent-encoding),以便直接使用任何想要的字符或格式。不過值得一提的是,這同樣也會大幅降低開發人員的使用體驗。

何時不該使用查詢字符串?

在實際應用中,由於查詢字符串往往是URL的一部分。而那些隱藏在客戶端和API之間的攻擊者(如:中間人,MIM)則可以輕而易舉地讀取到我們的URL,因此我們不應該簡單地將諸如密碼之類的敏感數據,直接放入查詢字符串之中。

雖然大多數HTTP客戶端在URL中都會允許使用五位數(five-figure)長度的字符,但是如果我們未能全面設計和考慮URL的整體長度,那麼開發人員在調試此類字符串時,往往也會經歷各種繁瑣和不便。

當然,由於我們能夠將任何內容都定義為資源,因此我們有時候完全可以使用POST端點,來進行大量參數的調用與傳遞,進而將報文中的所有數據都發送給API。

可見,為了避免向查詢字符串中那些具有多個參數的資源發送GET請求,進而導致冗長且不可識別的URL產生,我們可以設計出諸如搜索類型的資源,根據API的需求,來緩存各種計算的結果。同時,我們還可以向/searches端點發布新的請求。而該請求會將我們的搜索相關配置與參數保存在報文之中。通過返回一個搜索ID,以便我們使用它來獲取搜索的結果。

總結

作為API設計人員或架構師,我們所追求的優秀實踐,實際上就是要找出API的最理想使用方式,以及最簡單的實現用例。綜上所述,我們建議您注意如下兩個方面:

從一開始就能夠藉助Moesif(人工智慧API服務平臺)之類的服務,著手分析自身API的使用模式。 嵌套式資源雖然可以提高URL的可讀性,但是如果我們嵌套得太多的話,則會導致URL參數的冗長。因此,如果您發現自己創建了一個具有超大的查詢字符串端點,那麼我建議您最好從其中提取一部分子資源,放置到報文中作為參數發送出去。

原文標題:REST API Design Best Practices for Parameters and Query String Usage,作者:Kay Ploesser

【51CTO譯稿,合作站點轉載請註明原文譯者和出處為51CTO.com】

【責任編輯:

龐桂玉

TEL:(010)68476606】

點讚 0

相關焦點

  • REST API URI 設計 7 準則
    在今天的網站上,URI 設計範圍從可以清楚地傳達API的資源模型,如:http://api.example.com/louvre/leonardo-da-vinci/mona-lisa到那些難以讓人理解的,比如:http://api.example.com/68dd0-a9d3-11e0-9f1c-0800200c9a66Tim Berners-Lee
  • Swagger在Spring Rest API中的使用
    編寫了Spring rest apis後,與前端開發人員共享,以便他們可以與之集成。前端開發人員將需要所有其餘的api端點以及每個端點的請求方法,請求參數,請求正文和響應格式。你將如何分享有關您的api的所有信息?手動記錄所有api是非常困難和耗時的。此外,如果您手動記錄api,則每次在api中進行一些更改時都必須更改文檔。好!
  • 開發出優秀的API,構建RESTful API的13種最佳實踐,學會此文就很優秀了
    RESTful API定義了命名資源的最佳實踐,但定義了允許你修改資源/與之交互的固定HTTP操作。可以在RESTful API中訪問以下HTTP操作:在對RESTful API的特性有了更深入的了解後,是時候了解更多關於RESTful API的最佳實踐了。
  • 使用pyhttptest 輕鬆測試 REST API
    現在,我們每個人都面臨著 REST API,要麼開發這樣的服務,要麼使用這樣的服務。 此外,我們正處於微服務的時尚時代,我們將業務邏輯分割成獨立於每個服務的小型獨立服務。 這些服務大多遵循 RESTful 原則,並使用 JSON 格式進行通信,由於其簡單性,JSON 格式成為最廣泛使用的格式。
  • API 分頁設計與實現探討
    對於設計和實現 API 來說,當結果集包含成千上萬條記錄時,返回一個查詢的所有結果可能是一個挑戰,它給伺服器、客戶端和網絡帶來了不必要的壓力,於是就有了分頁的功能
  • 如何設計restful風格接口
    REST描述的是在網絡中client和server的一種交互形式;REST本身不實用,實用的是如何設計 RESTful API(REST風格的網絡接口);2. Server提供的RESTful API中,URL中只使用名詞來指定資源,原則上不使用動詞。「資源」是REST架構或者說整個網絡處理的核心。
  • Salesforce Rest Api詳解一之Spring Boot集成
    該博客介紹了使用REST API與Salesforce集成的方法。我們將使用Spring Boot進行身份驗證(從Salesforce獲取access_token和instance_url)。儘管可以使用普通的Java應用程式來完成此操作,但是我們將假設現實中的用例需要Web應用程式來調用服務。
  • 架構設計優雅編程之REST
    rest apiREST概念是什麼?它是一種針對網絡應用的設計和開發方式,可以降低開發的複雜性,提高系統的可伸縮性。前後端通信請求方式?REST要求客戶端向服務端發出請求以獲得或修改伺服器上的數據。HttpURLConnection詳解:HttpURLConnection是一種多用途、輕量極的HTTP客戶端,使用它來進行HTTP操作可以適用於大多數的應用程式。
  • 通過Kettle調用Rest API獲取信息
    【IT168 技術】作為目前主流Web服務交互方案,RESTful相比於SOAP(Simple Object Access protocol,簡單對象訪問協議)以及XML-RPC更加簡單明了,更加簡單輕量的方法設計和實現,使它逐漸成為軟體設計的一個最流行的方案。
  • RESTful API 設計規範
    你使用的標準樹需要取決於你開發的項目。未註冊的樹( x)主要表示本地和私有環境私有樹( prs)主要表示沒有商業發布的項目供應商樹( vnd)主要表示公開發布的項目後面幾個參數依次為應用名稱(一般為應用域名)、版本號、期望的返回格式。
  • HBase實踐篇 | 為HBase的Thrift 客戶端API設計連接池
    第一種是 Java 中常用的方式,官方在hbase-client包裡提供了豐富的 API,另一種是 HBase 的 thrift api,主要在跨語言環境中使用。基於上述因素,最後決定在 Java 環境中也使用 HBase Thrift 的 api。
  • Django REST Framework教程(6): 認證詳解及如何使用Token認證
    ', 'rest_framework.authentication.SessionAuthentication',    )}方式2:基於類的視圖(CBV)中使用from rest_framework.authentication import SessionAuthentication
  • JavaScript 的 API 設計原則
    如果是簡單的幾個無所謂,但是通常一套框架都有幾十甚至上百的api,映射成本增加會使得程式設計師哥哥崩潰。所以,大家都比較喜歡用jquery的api,雖然一個$符號並不代表任何現實意義,但簡單的符號有利於我們的使用。它體現了以上的多種原則,簡單,易讀,易記,鏈式寫法,多參處理。
  • 非RESTful 的微軟 REST API 指南
    客戶端應該不需要在URL中傳遞個人身份信息參數,因為它們可能會暴露在日誌文件中。參數應該通過頭信息傳遞。數據應該以流行的格式提供。默認數據編碼是JSON。「基本型數值必須遵循RFC4627標準序列化成JSON。」
  • 請不要再管它們叫 REST API 了
    造成誤解的主要原因是糟糕的命名方式、設計上的錯誤和不嚴格的標準,等等。所以說,導致這種誤解是很自然的。去年,我在推特上發了一條有關 REST 架構範式的推文。如果你問一下身邊的程式設計師他們設計的 API 是否支持 HATEOAS,他們十有八九會瞪大了眼睛看著你,然後說:你到底在說什麼?然而,REST 的名字本身就說明了一切。它並沒有說要使用哪一種協議,也沒有說使用哪一種方式來標識資源,它只說到表示性狀態轉移(REpresentational State Transfer)。
  • 如何基於Python實現MySQL查詢的API設計,附上完整腳本
    如果是基於Shell的方式,很容易出現一個使用瓶頸,那就是如果通過shell去查看一個表的數據,那麼輸出是沒有規範的格式的,Shell執行是最簡單最基本的調用模式,我們也可以利用資料庫服務端的特性來輸出相應的數據格式,但是基於資料庫版本的差異,有些低版本是不支持輸出一些格式的,所以使用Shell來輸出SQL查詢的結果顯然不是一個通用而且優雅的實現方式。
  • 網易CI/CD實踐(下):測試自動化及API版本管理
    因此,業內最佳實踐往往是加大最底層單元測試的佔比(70%),再配合一定的接口自動化測試(20%)以及集成測試(10%)。如果從 CI/CD 最佳實踐來看,單元測試自動化一般在 CI 階段就會 cover 掉,並作為代碼合入的標準。而在發布過程中,則主要是接口測試自動化,以及最終的集成測試自動化。
  • OpenAPI Generator v4.1.1 發布,OpenAPI 規範自動生成代碼
    4.1.1 版本已經發布,更新內容如下:General [core]設置 isMultiPart=true 用於多部分操作,修復 NPE 在服務中使用 anyof/oneof 時,修復導入和屬性名稱 自動更新生成腳本:userdef cache dir 修復了從 allVars 中丟失的組合屬性
  • RESTful API 最佳實踐(阮一峰)
    本文總結 RESTful 的設計細節,介紹如何設計出易於理解和使用的 API。這時,客戶端發出的 HTTP 請求,要加上X-HTTP-Method-Override屬性,告訴伺服器應該使用哪一個動詞,覆蓋POST方法。
  • 如何用 Go 快速編寫出 HTTP REST API 服務?
    Go模塊現在可以在GOPATH目錄下和目錄外使用。這也是我在本文選擇使用GO 1.13版本的原因。檢查許可證另外一個好的實踐是檢查許可證。你需要去檢查許可證(你項目依賴項使用的許可證),例如,當你希望你的應用程式或者公司的代碼開源時,為了避免使用被禁的許可證,就要去檢查。在Go中存在一個名為wwhrd的工具。