RESTFul API 的十大誡條

爭做團隊核心程序員,關注「幽鬼」

作者 | Vedran Cindrić

譯者 | Sambodhi

策劃 | Tina

來源 | InfoQ

本文最初發表於 Treblle 網站,經原作者 Vedran Cindrić 授權,InfoQ 中文站翻譯並分享。

我在過去十年裏,一直在研究的核心內容之一就是 API:從單一客戶端使用的簡單 API,到多種設備和多種用途的 API。在過去這些年來,我也有機會與很多第三方 API 一起工作,比如 Stripe、Twilio,以及其他一些不太流行但很有吸引力的 API。這些 API 幾乎都以 REST 爲基礎,有些地方是獨一無二的。

REST 之所以流行,有許多理由可以解釋。它易於理解、靈活、適用於各種規模,擁有龐大的社區,以及圍繞其構建的各種工具。但是,除此之外,我還要說,許多流行的理由都是源於其最老的競爭對手——SOAP,它實在是太可怕了。如果你曾經使用過或與 SOAP 一起工作過,你就會明白我爲什麼這樣說!它的客戶端非常糟糕。客戶端非常糟糕,人們可以隨心所欲地使用 XML,它非常煩瑣,有時候授權也非常奇怪…… 幸運的是,JSON 和 REST API 在這一戰中獲勝。

另一方面,還有一些更新的、更現代的參與者,嘗試着從基於 REST 的 API 中獲得一些關注。當然,我指的是 GraphQL。GraphQL 也是以 JSON 爲基礎,共享了 REST 的優勢,例如靈活性、性能、可擴展性等。對我個人來說,GraphQL 的兩個主要缺點是它是由 Facebook 構建的,並且它把 API 設計過程轉移到了客戶端。我的意思是,這使得移動開發人員和前端開發人員有責任創建自己特別的 API,並且可以通過數據庫進行查詢。我不清楚你的想法,但是我覺得這個想法不是最好的主意。別誤解我,我熱愛移動和前端開發,但是他們在數據庫設計、編寫查詢和構建 API 方面的知識上,也許並沒有太多的經驗。你也許不會要求你的後端開發人員設計一個應用,讓汽車修理師修理飛機,或是請獸醫爲人類進行手術。他們能做到嗎?或許,他們有可能。他們應該這麼做嗎?不,我不這麼認爲。

依我看,REST 依然是叢林中的王者,不會輕易被打倒。 REST 的唯一問題在於,歸根結底,它既不是標準,也不是協議。相反,它是一套 “架構約束”。這是一種不能稱之爲標準的花哨的方式,但是它卻能激起很多人的想象力。他們往往會按照自己的想法去做,或按照自己的(錯誤)理解去實現這些約束。爲了避免這樣的誤解,我決定把我所認爲的 REST API 十條戒律都寫下來。遵循這些戒律,你將會從你的移動開發人員那裏得到關愛,從後端開發夥伴那裏得到敬仰,在 Twitter 擁有搖滾巨星的地位。

因此,正如歌詞所唱的那樣:“Won't you follow me into the jungle ?”(你願意跟隨我進入叢林嗎?)

要務實

這其實很簡單。如果你正在構建 REST API,你應當接受並使用 JSON 進行響應。 不要用 XML。不要用別的什麼東西。就是 JSON。

沒錯,JSON 並非 “必須的”,因爲 REST 並不是一種標準,但這是你要做的事情之一。就好像你每天都要穿衣、睡覺,或者每天都要喝一杯咖啡。好吧,也許不是最後一部分,但你明白我的意思了。我們越早讓大家擺脫 XML,轉向 JSON,對大家來說就越有利。如果你不知道 JSON 爲什麼會更好,那就讓我隨便列舉一些理由吧:

如果你覺得這還是太過抽象,那麼我建議你從互聯網上隨便下載一個 XML 文件,然後試着對其進行解析。我在 2000 年代的時候已經有過很多次這樣的經歷,我可以向你保證,你會哭着回到 JSON 那裏去。你根本沒有理由在 2021 年使用 XML 而不是 JSON。當然,如果你有一個遺留的企業系統,那麼我對你的痛苦感同身受,但是即便如此,情況也在改變。越來越多的老舊的大公司不斷地對其內部和外部 API 進行升級,包括 XML、SOAP、JSON、REST 等。

JSON 不應該只在響應端使用,也應該在請求端使用。所以,不要使用 form-data 或 x-www-form-urlencoded 來發送數據,而是要使用 JSON 來發送。這使得閱讀、寫入、測試和管理都變得更加簡單。

記住,在不確定的情況下,請使用 JSON。 我們所有開發人員在此都會向你表示衷心的感謝。

有條理

你一定不會相信,我已經見過很多次 API 只使用 GET 方法來做從數據存儲到過濾的所有工作。作爲一名開發人員,你必須一直努力瞭解所使用的工具,因此,當你在開發 API 時,一定要知道 HTTP 的工作原理。每一種 HTTP 方法都是特定的情況而設計的。讓我們分別看一下,然後再決定何時應該使用它們。

在我們開始之前,先做一個小而有趣的澄清。在描述 HTTP 方法時,人們經常使用 “safe” 和“idempotent”這樣的詞。這些詞聽起來很好,也很神祕,但其實沒那麼複雜。用簡單的英語來說,safe 意味着只需準備好。你可以在不需要擔心更新、破壞或改變數值的情況下,向這個端點發送請求。而 idempotent 則意味着你可以向同一個端點發送多個請求,而不會改變任何內容或獲得不同的結果。通常,所有的 safe 方法同時也是 idempotent,但是並非所有的 idempotent 都是 safe。我知道一開始你會感到迷惑,但是我們會在後面說明。

GET 方法

當希望讀取數據時,你應該使用 GET 方法。該方法不是存儲,不是更新,也不是改變數據。它只讀取數據。這是一個非常簡單的概念,任何人都不應感到困惑。我們知道,GET 方法只能用來讀取數據,並且每次都會返回同樣的數據。我們可以說,GET 請求是安全的,也是可執行的。

POST 方法

當你發出 POST 請求時,全世界每個人都想讓你存儲一些信息。這意味着你將在數據庫創建一個新行,在某處寫點什麼,或者從無到有創造一些東西。如果你願意的話,你可以使用多種內容類型將數據發送到 POST 方法:或  或 raw(application/json,text/plain 等)。當構建 REST API 時,我推薦客戶端以  的形式發送數據。這樣我們就能保持一致,符合 JSON 精神,而且發送 JSON 數據可以讓你輕鬆地做出真正複雜的請求。最後,POST 操作是不安全的,因爲它們的確會在服務器端改變一些東西,而且它們也並非無所不能,因爲向同一個端點發出兩個請求會導致不同的資源。

PUT 方法

PUT 請求最常被用於更新的場合。它也可以用於創建新的記錄,但當時的設想是,客戶端必須是一個 ID,爲新的資源定義了一個 ID。所以,要讓你的工作更加簡單,在你需要更新一個資源時,請簡單地使用 PUT。PUT 顯然並非安全的操作,因爲它在服務器端做了改動,不過,你會很喜歡,它是 idempotent 的操作。

DELETE 方法

我想說的是,沒有必要對這個操作進行任何說明。如果要刪除資源,就直接使用 DELETE 方法。這個操作肯定不安全,但是有些人說它是 idempotent,也有些人說不是。

PATCH 方法

PATCH 請求用於再次更新資源,但與 PUT 不同的是,它只需要更新改變的數據,而 PUT 能夠並且應當對全部的資源進行更新。這是不安全的,也是不可行的。

既然我們已經瞭解了基礎知識,那麼接下來就是實際生活中所發生的事情。大部分人都是用 GET、POST,還有一些人使用 PUT 和 DELETE。我幾乎沒有見過有誰在用 PATCH。我推薦你使用一切可用的 HTTP 方法,因爲這就是它們的目的。你可以把所有的 CRUD 操作映射到 POST、GET、UPDATE 和 DELETE。我只想讓你別用 GET 來創建或更新數據。請不要這樣做!

重語義

這也許是你唯一一次聽到我對別人建議,要注重語義。但是,在這種情況下,這個問題很重要,並且涉及正確命名的問題。我經常會在 API 文檔中發現一些可怕的命名約定。我認爲,每個優秀的 REST API 都應該能讓一般人容易理解。從端點名稱到輸入參數,再到 JSON 鍵。

所以讓我們從 API 端點開始。規則也非常的簡單:

之所以要使用名詞,是因爲當你進行 HTTP 請求時,就像我們前面提到的,你使用的是動詞。每個 HTTP 方法(GET、POST、PUT、PATCH)在英語中都是一個動詞。所以,用雙重動詞就沒有任何意義了,對吧!如果你將 API 端點命名爲 /getUsers,並且你在進行一個 GET 請求來查看所有的用戶,那麼你讀這個語句的時候會覺得很有趣。GET getUsers。沒有意義。

我經常見到的另一種常見情況是用單數而不是複數的端點名稱。這當然大錯特錯。你需要 API 的端點保持一致、簡單和邏輯性。如果你使用了複數,你可以爲每一種 HTTP 方法提供同樣的 URI。如果你做的是 GET /users,那麼你將會把所有的用戶都列出來,而你在做 POST /users 時,你就會創建一個用戶。因此,同樣的 URI,使用不同的 HTTP 方法(動詞)。更酷的是,你可以請求 GET /users/:id 來獲取信息,以便了解更多的細節。所以,就像你所見:它仍然是相同的起始資源名稱,只不過有了更多的深度。假如你使用單數,那麼 GET /user 表示你想要擁有一個用戶,並且你還需要在其他場合中使用更多的 URI。複數就更有意義了。

回過頭來,讓我們來看一些良好的示例和糟糕的示例,這樣我們就能 100% 地瞭解了。

良好的示例:

槽糕的示例:

我認爲你對這種觀點有了一定的瞭解。這真的非常簡單,通過以上示例你可以瞭解哪些聽起來不錯,更有意義。

還有一點關於端點命名的補充說明:儘量使用單個單詞而不是多個單詞。 如果你一定要使用多個單詞,那麼在它們之間使用連字符。看在老天爺的份上,在 URI 中使用所有的小寫字母。

作爲本節的總結,我將簡單地介紹一下 JSON 鍵在請求和響應數據中的命名規則。對於這一問題,存在着大量爭議,特別是考慮到有三種可能的情況:camelCase(駝峯式大小寫)、snake_case(蛇形命名法) 和 spinal-case(脊柱命名法)。同樣,沒有人可以阻止你使用其中任何一種,從理論上講,你是不會出錯的。不過,我推薦使用 snake_case。 爲什麼?Stripe 使用它。PayPal 使用它。Facebook 也使用它。你知道嗎?就我個人而言,我發現這樣我們就可以 100% 地弄清楚了:因爲更容易閱讀,這就是爲什麼我們所有的 API 都使用 snake_case。還有一些類似的研究顯示,snake_case 在可讀性上要優於 camelCase。

保安全

我就直截了當地說:如果你在 2021 年沒有使用 HTTPs,那麼你就太可恥了。 你的 REST API 應該在 HTTPs 上運行,不會有任何問題。使用 HTTPs 只是提供了一個 HTTP 所沒有的安全元素。它使你的用戶免受中間人攻擊,並對客戶端和 API 之間的通信進行加密。蘋果、谷歌等其他大公司不久就會強迫你啓用 HTTPs。最重要的是,如今你可以免費獲得一個 SSL 證書。可以通過亞馬遜雲科技和 Rout 53 等類似的服務,也可以通過類似 Azure 或者 Let's encrypt 的服務來實現。它們都很好用,我可以親自證明這一點。你還可以隨時購買一個高級 SSL 證書。不管你怎麼做,只要將 HTTPs 用於你的下一個 API。你以後會感謝我的。

另外一件令我非常傷心的事就是,我發現一個 API 不需要任何形式的授權。如果你創建了 API,你可以簡單地控制哪些人可以訪問。這是非常容易的,但是很多人並沒有這麼做,因爲他們覺得實施、使用和維護都非常的煩瑣。其實大可不必。你可以先用不記名的令牌來開始,這個設置只需要 2 分鐘即可完成。即使你不想要,也不必將其與數據庫相連。如果你願意,你可以轉而使用 JWT 或者 oAuth 等更爲成熟的解決方案。你應該需要某種認證解決方案是有許多理由的。首先,你可以控制 API 的訪問權限和用戶能夠訪問的數量。如果有問題,例如你的 API 被垃圾郵件、黑客攻擊,或者其他,你只需關閉被公開的密鑰。你也可以使用 API 密鑰來跟蹤 API 的集成情況,看看有沒有用戶過度調用 API,或者客戶端的行爲不正常。最終,你還可以通過 API 調用來收集客戶端、用戶和 API 的統計數據。

我的觀點是:把 API 當作自家房子來看待。 我敢肯定你有幾扇有鑰匙的房門,而你只是將這些鑰匙交給了重要的人。對待你的 API 也應該一樣。

另外,你還需要注意的是,在網絡上發送超級敏感的數據。我們討論了這個問題,並討論了 SSL 用於通信加密的重要性。這將是第一步。第二步是,不要傳回那些可能不會在你的應用或者你的網站上使用的敏感數據。我指的是用戶地址、電話號碼、SSN(譯註:美國社會保險號)或者其他形式的身份認證。如果不用,就不要傳回。如果你使用了,那麼就要保證訪問 API 和得到響應的人是你正在傳回數據的真正用戶。我知道,這聽上去很簡單,但是實際上,人們會做出很多瘋狂的事。這主要是他們覺得 “哦,不會有任何事情發生,這只是一個很小的 API,沒有人知道。” 在這一點上請相信我。人們會發現的,只要你讓別人去做,別人就會去做。因此,請別這麼做。

在我們結束之前,我要談談 UUIDs 與 IDs 之間的辯論。我是 ID 的長期粉絲,因爲它更簡短,也更快速,但是我認爲 UUID 在安全性和隱私優勢更加重要。UUIDs 的安全性更高。你可以在數據庫中增加自動遞增的 ID 列,但如果你將模型公開給 API,則可以使用 UUIDs。這些忠告都是簡短的,但是卻能幫你省去許多麻煩。

最後我要說的是基本的基礎設施安全。如果你使用的是亞馬遜雲科技或 Azure,那麼這就是你的優勢。API 網關爲你提供了更多的安全性,可以通過防火牆和偵測 DDoS 攻擊。你能用就用吧。只要你能阻止某個特定的 IP 或用戶,你就會尋找這樣的東西。如果你在傳統服務器上運行 Apache,下面有兩個非常快捷的建議,可以幫助你在接下來的日子裏省去很多麻煩。

第一條建議很簡單:堅持更新你的軟件。Apache、PHP、Composer 包、NPM 包。所有的軟件都要更新。一定要保證你使用的軟件都是最新的、最好的。

第二條建議,Apache 默認向每一次請求發送一個響應頭,它將告知潛在攻擊者你正在使用的 Apache 的哪個版本。 這個響應頭的鍵稱爲 server,其默認值可能是這樣的:Apache/2.4.1(Unix)。你現在要做的就是迅速地把 Apache 的版本隱藏起來。

只需打開:/etc/apache2/conf-enabled/security.conf,將 ServerTokens 的值設爲 Prod。之後,運行 sudo systemctl restart apache2,您將會讓您的服務器變得更加安全。恭喜。

當你打開 /etc/apache2/conf-enabled/security.conf 時,你可以做的另一件事是關閉 ServerSignature。只要把它設置爲 “Off”,你將會變得更加安全。

通常情況下,每個季度都應該要召開一次安全會議,討論如何改進安全、怎樣改進,以及怎樣保證安全。你不想受到黑客、DDoS 等攻擊。你要相信我。

有組織

除了修改 API 的版本之外,還有什麼更好的辦法來保持組織性呢?我知道你們已經閱讀過很多篇關於這一問題的文章,覺得 “我的 API 太小,而且只有一位用戶在用,因此我不會去使用版本管理。” 我曾經和你一樣,我以前也是這麼幹的。我也這麼說過。要明智,在 API 中使用版本管理,是你能儘早做出的最佳決策。

我之所以遲遲不進行版本管理,是因爲在我看來,在 API 層面上,從 V1 到 V2 的跳躍很少發生。我說的很少,是指技術方面:因此,每年都有一次我會了解到,版本管理對於我而言毫無意義。但是,我這一想法是錯誤的。當然,大版本跳躍並不經常發生,但當你擁有一個平臺或者一個正在使用或者開發中的應用時,你就會做一些小的升級,但是頻率會更高。我指的是對模式、數據類型、結構或者流程的每週更新,或者每月更新,這對於那些沒有更新應用程序或者其他事情的人是很危險的。爲了完成這一切,在你沒有啓用版本管理的情況下,你在每次完成 GIT 提交時都會大汗淋漓。你不但要確定你的代碼沒有破壞任何東西或任何人,還要了解某個版本的應用程序的將會如何表現。相信我,這些一點都不好玩。

只需爲你和你的團隊挑選一個最有意義的版本管理方案。在軟件領域裏,最普遍的版本管理方案就是:major.minor.patch。在我看來,PATCH 部分有點多,但是你可以選擇這個模式。我通常會從 v1.1 開始,然後一直到 v1.9。因此,對於大版本變化來說,你是在改變 API 的核心內容,例如認證、核心模式、流程等等。對於小版本和補丁版本來說,你通常做的就是增加或者刪除某些小的特性,以某種方式破壞數據結構或類似的東西。

另外一個令你感到迷惑的問題是,由於有許多途徑可控選擇,所以你該如何真正地實施版本管理。你可以通過以下方式使用版本管理 URI 路徑、請求頭、查詢參數或內容協商。現在,這些方式各有利弊,但是我推薦使用 URL 的版本管理。它是最有意義的,從許多方面來說,這是最容易理解的。通過更新,可以知道哪些版本在使用,只是從 URL 到指向正確的文檔版本。REST APIs 基本上是以 URI 爲基礎的,我想我們應該保持這一傳統,使用基於 URI 的版本管理。這方面的一些例子包括:

總結一下,儘早開始使用版本管理,你可以在將來避免出現問題,並且可以擴展您的 API。

要一致

你可能聽說過這句話。“一致性是將平庸轉化爲卓越的原因”。無論你相信與否,這句話在生活和 API 設計中都是真的。優秀的 API 的一個特點就是它的一致性。首先,我的目標是資源 / 模型的一致性,然後是其他領域,如命名、URI、HTTP 代碼和類似的。

正如我們現在所知道的,API 可以歸結爲資源。一種資源可以是任何東西,包括用戶、文章、書籍、產品等等。每一種資源都可以包含多個屬性、對象或數組。資源是結構化的,基於你在數據庫中的數據或其他業務邏輯。你的 API 要取得成功,關鍵在於保持你的資源響應。你無法將你的端點返回完全不同的資源結構。雖然這聽上去很有吸引力,也可能是優化事物的方法,但是你最好不要這麼做。實施你的 API 的移動開發人員將遵循你所提供的結構,就如同遵循了《聖經》。如果你在每個端點上發送不同的東西,那麼他 / 她的日子就會很糟糕,沒有人希望這樣。所以,要儘量總是發送相同的資源結構。如果你沒有數據,則將其作爲空值,或者對象,或者數據來發送。讓我們來談談現實,假定我們有 “文章” 資源,有時候文章可能會有評論,而有時候卻沒有評論。有時候,加載評論是有意義的,而有時卻沒有意義。這很好,但是請試着在你的結構方面作出一致的響應。

當你得到一篇文章時,你想加載評論,就像這樣:

{
   "status":true,
   "message":"Getting details for article with UUID: 5b8f6db5-7848-490e-95a7-f7146dd2e30c",
   "article":{
      "title":"Sample title 1",
      "description":"Sample description 1",
      "image":"https://domain.com/image-1,jpg",
      "uuid":"eec33d99-e955-408e-a64a-abec3ae052df",
      "comments":[
         {
            "text":"Great article",
            "user":{
               "name":"John Doe",
               "uuid":"5b8f6db5-7848-490e-95a7-f7146dd2e30c"
            }
         },
         {
            "text":"Nice one",
            "user":{
               "name":"Jane Doe",
               "uuid":"2ececb69-d208-46c2-b560-531cb716d25d"
            }
         }
      ]
   }
}

但如果你在下載一系列的文章,或是你剛創建了一篇沒有任何評論的文章,那麼你應該返回以下內容:

{
   "status":true,
   "message":"Article list was a success",
   "articles":[
      {
         "title":"Sample title 1",
         "description":"Sample description 1",
         "image":"https://domain.com/image-1,jpg",
         "uuid":"eec33d99-e955-408e-a64a-abec3ae052df",
         "comments":[]
      },
      {
         "title":"Sample title 2",
         "description":"Sample description 2",
         "image":"https://domain.com/image-2,jpg",
         "uuid":"b8ee70a8-1128-4670-9368-83953fdf722b",
         "comments":[]
      }
   ]
}

因此,如果客戶端期望看到一個評論數組,它們仍然會得到它,但它將只是空的。這樣一來,你將不會更改你的模型,也不會刪除對象 / 數組。

你只是通過保持一致,就爲自己和他人節省了大量的時間。

要優雅

如果你正在構建 API,就不可避免地會發生一些問題。這很好。這也是意料之中的事。你不必因爲 API 的錯誤或問題而感到難過。

如果你的 API 有一個錯誤或問題,你不應該感到難過。如果您不提供詳細信息,並且確保您的 API 比其他任何人都要聰明,那麼您應該感到不安。

從最上面開始,我發現了一個最常被開發人員使用的事情,就是 HTTP 狀態代碼。如果你不瞭解,那麼 HTTP 的狀態代碼可以在任何你能想到的情況下使用。你只要知道用哪個,然後再返回到客戶端。HTTP 相應狀態代碼有 50 多種,每一種都有其特殊的意義,需要在特定的環境中加以應用。我將不一一列舉,但是我們將列舉一些最常用的。我們已經有:

因此,你真的擁有你需要的所有狀態。從 OK、未授權、未找到、內部服務器錯誤到超文本咖啡壺控制協議,所有這些都有。是的,最後一個是一個實際的狀態。這就證明了製造 HTTPs 的人是最會開玩笑的人。

無論如何,每個狀態都有其意義。這種意義被廣泛地接受和了解。 所以,無論是中國的開發人員還是德國的開發人員,都會理解,當你發送 401(未授權)的狀態時,意味着客戶端沒有發送正確的認證信息。因爲我們響應的狀態代碼是 401(未授權),所以大家都知道這是客戶端故障,必須通過客戶端來解決,而非 API。我只是舉一個例子,但我的意思是,你應該在適當的情況下使用適當的 HTTP 狀態代碼。使用它們可以幫助你的 API 得到廣泛的理解、一致和標準。雖然 REST 並不是一個標準,但它是你必須遵守的一個標準。

一旦有了 HTTP 的狀態代碼,我們就必須在遇到困難時給客戶提供儘可能多的詳細信息。爲此,我們需要做許多工作。首先,我們要能預想到 API 的失敗,別人會怎麼做,哪些事情不會發生,哪些人會違反這些規則。因此,第一步要進行強有力的、嚴密的數據驗證,尤其是在創建內容之前。當你得到數據後,你需要檢查這些數據是否有效。這就意味着喲啊檢查 ID 是否真實存在,數值是否符合我們的預期,並且可以把它存儲到數據庫中。完成以上操作,並對相應的狀態代碼作出響應,將會使你的 API 讓人用起來非常愉悅。因此,假定你已經有了一個端點,它接受了 user_id 並獲得了用戶資料。如果我們應用預測可能發生的事情的策略,我們會做以下的事情:

  1. 檢查請求中是否有 user_id 參數:如果沒有,則迴應 400 錯誤請求。

  2. 檢查給定的 user_id 是否真的存在於系統中:如果沒有,則以 404(未找到)迴應。

  3. 如果 user_id 返回一個結果,則響應 200(OK)。

正如你所看到的,我們擁有多種故障保險,在所有這些故障中,我們都用正確的、可理解的響應代碼進行響應。

最後,一旦我們設置了我們的響應代碼,並且預測了 API 可能出現的故障,我們只需要儘可能地表達出來。我知道,對於我們的開發人員而言,這是一件非常困難的事,但是請相信我,這是你能做的最好的事情之一。當事情出錯時,REST API 就會有一個通用的錯誤響應模型。如果我們已經有了這種模型,客戶端開發人員就可以依賴於此向用戶提供關於哪裏出了問題的更詳細的解釋。那麼,讓我們來想象一下,有一位用戶在自己的手機中發送了一封無效的電子郵件。它以某種方式被傳送到 API,API 自然會出發一次確認和錯誤,並且響應 400(錯誤請求)。與此同時,API 應當發出一種通用的錯誤響應模式,使客戶端能夠將任意或全部的信息顯示給終端用戶。所以,如果是這樣的話,你很有可能會返回一個錯誤信息:“輸入的電子郵件地址無效”。客戶端可以讀取並將其顯示給用戶。同樣,我們需要確保你能夠涵蓋所有的問題,包括從驗證到服務器的故障。要實現這個目標,我們最好能找到一種適用於各種情景的通用錯誤模式。我推薦你採用下列方法:

{
   "title":"Invalid data",
   "message":"The data you entered is not valid",
   "errors":[
      {
        "field": "email",
        "message":"The email address you provided is not real"
      },
      {
        "field": "phone_number",
        "message":"You did not enter a phone number at all"
      }
   ]
}

JSON 的錯誤結構非常簡單。我們有一個標題和消息,爲我們提供了一個大致的方向,很多時候,開發人員並沒有把全部的錯誤信息展示給最終用戶。他們可以在 iPhone 中設置一個警報模式,進現實我們的消息或者標題。但是我們還會發送一個錯誤數組,它可以容納具有特定信息的特定錯誤。提供詳細的錯誤信息將有助於你和其他在 API 上工作的開發者瞭解到底是什麼出了問題。即使你沒有講這些信息展示給最終用戶,你也可以在你的要求下,或者你正在使用 Treblle 這樣的服務,來看到這些信息。

所以一定要記得,你要儘可能去預測更多的問題。 提供大量的細節,說明爲什麼事情會發生失敗,即便是沒有人使用,並且使用普遍理解的 HTTP 響應代碼的語言。

要聰明

這是一個更具哲理的問題,但是我認爲,這是優秀的 REST API 的支柱之一。假如你想想 API 在現代平臺、服務或應用程序中的作用,那麼我們可以說 API 是整個操作的大腦。 理由是,你可能擁有的每一個客戶端(iPhone 應用、安卓應用、網站、物聯網設備)都會與同一個 API 對話。這意味着我們的 API 在整個生態系統中起着舉足輕重的作用,我們的 API 尅解決所有問題。如果我能再加一句,那就是優雅。

一個聰明的 API 首先 要做的就是保護自己最有價值的資源——數據庫。這意味着它應該對數據庫進行淨化、清理,並防止任何不良數據進入數據庫。要做到這一點,一定要確認你從應用向客戶端發出的一切,並且排除一切看上去不合適的東西。但我們在拒絕某些東西時,也要給用戶一個清楚的原因,讓他們明白爲什麼會這樣,或者爲什麼在這種情況下沒有發生。

任何優秀、聰明的 API 都能獨立處理複雜的流程,而非依靠客戶端。 最簡單的例子是,將一個用戶註冊到你的應用中。對於所有的客戶端,這應該是一個 API 調用。但在後端,API 可以處理所有可能的後勤工作:在 MailChimp 通訊上註冊該用戶,向 Firebase 存儲推送令牌,向用戶發送歡迎郵件等等。客戶端段不應該爲這些事情調用多個 API 端點。如果我們把所有東西都打包到一個端點,那麼你就可以很容易地在任何時間點改變流程,而客戶端根本無需察覺。API 能夠始終對整個商業邏輯和流程進行全面的控制,只要你能從它們那兒獲得所需的數據。

請確定 API 是否經過了優化以滿足跨平臺的需求。 當你處理多個平臺或設備,如 iPhone、安卓手機、電腦、電視和類似設備時,API 應該能夠適應它們。這就意味着 API 必須具有足夠的靈活性,以應對可能與其他客戶端的不同輸入,同時也可以讓客戶端繼續工作。這方面一個很好的例子就是,可調節尺寸的照片,如果你有一個 API,可以提供照片的內容,那麼你也許不會希望將 4000x4000 像素的圖片發送到手機上,但是你可以將它發送到電視或者網站上。一個聰明的 API 會知道哪個客戶端在訪問這個端點,並會返回它自己的優化資源,甚至允許客戶端請求特定的尺寸。這樣的例子還有很多,不過你會明白的。

儘可能減少客戶端的依賴性。一個聰明的 API 總是會把客戶端的缺陷考慮進去,並且儘量改正。

須簡練

如果 API 不能迅速地進行優化,那它算得了什麼?in 的 API 不能成爲整個生態系統的痛點。就這麼簡單。有許多方法可以保證你的性能和擴展性都是優秀 API 所應有的。讓我們看一下其中的一些。

快速和優化始於數據庫層面。 每當我聽到有人說他們的 API 很慢時,十有八九與數據庫有關。數據庫設計不佳、查詢複雜、基礎設施遲緩、緩存不足。你應當總是對數據庫結構、查詢、索引以及其他與數據庫交互作用的所有內容進行優化。當你完成了這一任務之後,接下來要做的就是保證你能夠儘可能地提供高速緩存的數據。有時候,緩存數據庫查詢會使你的加載時間縮短 100%。所以,加入你有 API 斷電,可以向用戶展示他們的個人資料或者類似的內容,我敢肯定,它不會在每 5 分鐘改變。聰明一點,把數據緩存起來。你的用戶和調用這個 API 的客戶端將會很高興。

另外一個影響性能的因素是,你將大量的數據通過 API 下發到客戶端。一定要保證你的資源和模式只能轉回客戶端需要的數據,而非全部數據。如果你要返回一個項目的集合,可能你並不需要完全瞭解模式細節,以及它們之間的關係——每次都是如此。如果這樣會加快進度,就這麼辦。但始終要努力與響應中返回的模型保持一致。這意味着,如果你不加載關係,就返回一個空數組,如果你不加載計數,就返回 0,等等。在建立優秀的 REST APIs 時,一致性是關鍵。

你可以做的另一件非常簡單的事情是啓用壓縮,以減少響應大小並提高性能。最流行的是 Gzip 壓縮。在 Apache 上,它默認啓用了一個叫做 mod_deflate 的模塊。當啓用時,你通常可以看到一個響應頭的 Accept-Encoding 設置爲 gzip、compress 之類的。儘管它是默認啓用的,你仍然需要將它 “應用” 到你的 API 響應中。要做到這一點,我們必須在我們的默認 Apache 配置中添加一些規則。因此,讓我們實際打開配置文件,像這樣:sudo nano /etc/apache2/conf-available/000-default.conf 並添加這個美麗的東西。

這告訴 Apache 對各種 mime 類型應用 Gzip 壓縮,如 application/javascript、application/json 和其他。一旦我們保存了這個,我們只需要通過輸入 sudo systemctl restart apache2 來重啓 Apache,我們就完成了。你的 API 響應現在比以前小了 50~75%。這有多簡單啊!?

要周到

最後這一塊我就不多說了,因爲我們在 REST 方面已經做得太多了(永遠不會過時😀),但我不打算降低這個問題的重要性。開發 API 的過程對我們的後端開發人員是很寂寞的。這基本上就是你,一大堆代碼和你喜愛的測試工具。當你完成最後一行代碼,並把它與生產分支合併的時候,你也許會舉的工作已經結束了。但事實並非如此。對於其他許多人來說,旅程纔剛剛開始。

有許多人只是等你把工作做完了再去幹。要想讓他們更好地工作,你就必須在很多方面對 API 進行充分的準備。你要保證這個功能有效,文檔很好,而且你還得做好了提供集成支持的準備。無論你的文檔編寫的多麼出色,在集成過程中和集成後都會出現問題、困惑和難題。

設身處地爲他人着想,儘量讓他人的工作更加輕鬆。構建一個良好的 API,遵循我們在這裏定義的規則,編寫優秀的文檔,服務於他人。

考慮一下這些工具,這些工具可以幫助你解決很多構建、傳輸和運行 API 的問題,這些問題都很有挑戰性。這些工具之一就是,你猜對了,Treblle。只要把 Treblle 添加到你的 API 中,你就可以得到自動生成的支持 OpenAPI 規範的文檔。然後你會得到實時監控和記錄,這樣你就可以瞭解別人在做什麼,從而更好地幫助他們。最後,你甚至會得到一個基於這些相同規則的高質量的 API 核心。當然,我們在設計時考慮到了整個團隊。

希望我能夠簡單的說明一下你在構建 REST API 時可能會遇到的疑惑和擔憂。我必須指出的是,REST 並不是一個標準,因此沒有人能說你的錯誤。不過,請考慮一下:作爲開發人員,我們每天都在尋求讓代碼更好、更漂亮、更高效的模式,何不對 API 也這麼做?如果我們開始遵循一些模式,那麼我們都會享受一個更健康、更漂亮的生態系統。

最後,我要引述一位偉人說過的話來結束本文,他就是我們大家的叔叔——Ben Parker。他曾經睿智地說過:“能力越大,責任越大”With great power comes great responsibility)。當我們談論 REST API 時,這句話再正確不過了。

作者簡介:

Vedran Cindrić,Treblle 創始人。

原文鏈接:

https://treblle.com/blog/the-10-rest-commandments

歡迎關注「幽鬼」,像她一樣做團隊的核心。

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