一把梭:REST API 全用 POST?

寫這篇文章的原因主要還是因爲 V2EX 上的這個貼子 [1],這個貼子中說——

“對接同事的接口,他定義的所有接口都是 post 請求,理由是 https 用 post 更安全,之前習慣使用 restful api ,如果說 https 只有 post 請求是安全的話?那爲啥還需要 get 、put 、delete ?我該如何反駁他。”

然後該貼中大量的回覆大概有這麼幾種論調:

1)POST 挺好的,就應該這麼幹,溝通少,

2)一把梭,早點幹完早點回家,

3)吵贏了又怎麼樣?工作而已,優雅不能當飯喫。

雖然評論沒有一邊倒,但是也有大量的人支持。然後,我在 Twitter 上嘲諷了一下,用 POST 幹一切就像看到了來你家裝修工人說,“老子幹活就是用釘子釘一切,什麼螺絲、螺栓、卡扣、插銷…… 通通不用,釘槍一把梭,方便,快捷,安全,幹完早回家…… 不過,還是有一些網友覺得用 POST 挺好的,而且可以節約時間。所以,正好,我在《我做系統架構的原則》[2] 中的 “原則五” 中反對 API 返回碼無論對錯全是 200 的返回碼,我專門寫下這一篇文章,以正視聽。

這篇文章主要分成下面這幾個部分:

  1. 爲什麼要用不同的 HTTP 動詞?

  2. Restful 進行復雜查詢

  3. 幾個主要問題的迴應

    1) 爲什麼 API 要 Restful,並符合規範?

    2) 爲什麼 “過早優化” 不適用於 API 設計?

    3) POST 更安全嗎?

    4) 全用 POST 可以節省時間溝通少嗎?

    5) 早點回家的正確姿勢     6) 工作而已,優雅不能當飯喫

1、爲什麼要用不同的 HTTP 動詞

編程世界通常來說有兩種邏輯:“業務邏輯” 和 “控制邏輯”。

網絡協議也是一樣的,一般來說,幾乎所有的主流網絡協議都有兩個部分,一個是協議頭,一個是協議體。協議頭中是協議自己要用的數據,協議體纔是用戶的數據。所以,協議頭主要是用於協議的控制邏輯,而協議體則是業務邏輯。

HTTP 的動詞(或是 Method)是在協議頭中,所以,其主要用於控制邏輯。

下面是 HTTP 的動詞規範,一般來說,REST API 需要開發人員嚴格遵循下面的標準規範(參看 RFC7231 章節 4.2.2 – Idempotent Methods[3])

ZFctHg

其中, PUTPACTH 都是更新業務資源信息,如果資源對象不存在則可以新建一個,但他們兩者的區別是, PUT 用於更新一個業務對象的所有完整信息,就像是我們通過表單提交所有的數據,而 PACTH 則對更爲 API 化的數據更新操作,只需要更需要更新的字段(參看 RFC 5789[4])。

當然,現實世界中,可能並不一定嚴格地按照數據庫操作的 CRUD 來理解 API,比如,你有一個登錄的 API /login 你覺得這個 API 應該是 GETPOSTPUT 還是 PATCH ? 登錄的時候用戶需要輸入用戶名和密碼,然後跟數據庫裏的對比(select 操作)後反回一個登錄的 session token,然後這個 token 作爲用戶登錄的狀態令牌。如果按上面表格來說,應該是 select 操作進行 GET ,但是從語義上來說,登錄並不是查詢信息,應該是用戶狀態的更新或是新增操作(新增 session),所以還是應該使用 POST,而 /logout 你可以使用 DELETE這裏相說明一下,不要機械地通過數據庫的 CRUD 來對應這些動詞,很多時候,還是要分析一下業務語義。

另外,我們注意到,在這個表格的最後一列中加入了 “是否冪等” 的,API 的冪等對於控制邏輯來說是一件很重要的事。 所謂冪等,就是該 API 執行多次和執行一次的結果是完全一樣的,沒有副作用。

冪等這個特性對於遠程調用是一件非常關鍵的事,就是說,遠程調用有很多時候會因爲網絡原因導致調用 timeout,對於 timeout 的請求,我們是無法知道服務端是否已經是收到請求並執行了,此時,我們不能貿然重試請求,對於不是冪等的調用來說,這會是災難性的。比如像轉帳這樣的業務邏輯,轉一次和轉多次結果是不一樣的,如果重新的話有可能就會多轉了一次。所以,這個時候,如果你的 API 遵從了 HTTP 動詞的規範,那麼你寫起程序來就可以明白在哪些動詞下可以重試,而在哪些動詞下不能重試。如果你把所有的 API 都用 POST 來表達的話,就完全失控了。

除了冪等這樣的控制邏輯之外,你可能還會有如下的這些控制邏輯的需求:

也許,你會說,我的業務太簡單了,沒有必要搞這麼複雜。OK,沒有問題,但是我覺得你最差的情況下,也是需要做到 “讀寫分離” 的,就是說,至少要有兩個動詞, GET 表示是讀操作, POST表示是寫操作。

2、Restful 複雜查詢

一般來說,對於查詢類的 API,主要就是要完成四種操作:排序,過濾,搜索,分頁。下面是一些相關的規範。參考於兩個我覺得寫的最好的 Restful API 的規範文檔,Microsoft REST API Guidelines[5],Paypal API Design Guidelines[6]。

另外,對於一些更爲複雜的操作,建議通過分別調用多個 API 的方式來完成,雖然這樣會增加網絡請求的次數,但是這樣的可以讓後端程序和數據耦合度更小,更容易成爲微服務的架構。

最後,如果你想在 Rest 中使用像 GraphQL 那樣的查詢語言,你可以考慮一下類似 OData[7] 的解決方案。OData 是 Open Data Protocol 的縮寫,最初由 Microsoft 於 2007 年開發。它是一種開放協議,使您能夠以簡單和標準的方式創建和使用可查詢和可互操作的 RESTful API。

3、幾個主要問題的迴應

下面是對幾個問題的直接回應

1)爲什麼 API 要 Restful,並符合規範?

Restful API 算是一個 HTTP 的規範和標準了,你要說是最佳實踐也好,總之,它是一個全世界對 HTTP API 的一個共識。在這個共識上,你可以無成本地享受很多的技術紅利,比如:CDN,API 網關,服務治理,監控…… 等等。這些都是可以讓你大幅度降低研發成本,避免踩坑的原因。

2)爲什麼 “過早優化” 不適用於 API 設計?

因爲 API 是一種契約,一旦被使用上,就很難再變更了,就算你發行新的版本的 API,你還要驅動各種調用方升級他們的調用方式。所以,接口設計就像數據庫模式設計一下,一旦設計好了,未來再變更就比較難了。所以,還是要好好設計。正如前面我給的幾個文檔——Microsoft REST API Guidelines[5],Paypal API Design Guidelines[6] 或是 Google API Design Guide[8] 都是讓你好好設計 API 的不錯的 Guidelines.

3)POST 更安全嗎?

不會。

很多同學以爲 GET 的請求數據在 URL 中,而 POST 的則不是,所以以爲 POST 更安全。不是這樣的,整個請求的 HTTP URL PATH 會全部封裝在 HTTP 的協議頭中。只要是 HTTPS,就是安全的。當然,有些網關如 nginx 會把 URL 打到日誌中,或是會放在瀏覽器的歷史記錄中,所以有人會說 GET 請求不安全,但是, POST 也沒有好到哪裏去,在 CSRF[9] 這個最常見的安全問題上,則完全就是針對 POST 的。安全是一件很複雜的事,無論你用哪方法或動詞都會不能代表你會更安全。

另外,

4)全用 POST 可以節省時間減少溝通嗎?

不但不會,反而更糟糕。

說這種話的人,我感覺是不會思考問題。

5)早點回家的正確姿勢

不要以爲你回家早就沒事了,如果你的代碼有這樣那樣的問題,別人看懂,或是出誤用了你的代碼出了問題,那麼,你早回家有什麼意義呢?你一樣要被打擾,甚至被叫到公司來處理問題。所以,你應該做的是爲了 “長期的早回家”,而不是 “短期的早回家”,要像長期的早回家,通常來說是這樣的:

6)工作而已,優雅不能當飯喫

迴應兩點:

其一,遵循個規範而已,把 “正常” 叫“優雅”,可見標準有多低。這麼低的標準也只能“爲了喫飯而生存了”。

其二,作爲一個 “職業程序員”,要學會熱愛和尊重自己的職業,熱愛自己職業最重要的就是不要讓外行人看扁這個職業,自己都不尊重這個職業,你讓別人怎麼尊重?尊重自己的職業,不僅僅只是能夠獲得讓人羨慕的報酬,而更是要讓自己的這個職業的更有含金量

希望大家都能尊重自己從事的這個職業,成爲真正的職業化的程序員,而不是一個碼農!

參考資料

[1]        https://www.v2ex.com/t/830030?p=1
[2]        https://coolshell.cn/articles/21672.html
[3]        https://www.rfc-editor.org/rfc/rfc7231#section-4.2.2
[4]       https://tools.ietf.org/html/rfc5789
[5]       https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md
[6]        https://github.com/paypal/api-standards/blob/master/api-style-guide.md
[7]        https://www.odata.org/
[8]       https://cloud.google.com/apis/design
[9]       https://en.wikipedia.org/wiki/Cross-siterequestforgery

作者:陳皓

來源:https://coolshell.cn/articles/22173.html

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/dt-3GCJek5dNgxMhOBLuKA