來源 https://mccxj.github.io/blog/20130530_introduce-to-rest.html#disqus_thread ## API https://tw.alphacamp.co/blog/api-introduction-understand-web-api-http-json ## URL、URI、URN https://kim85326.github.io/2019/10/30/URI-URL-URN-%E5%B7%AE%E5%88%A5/ ## REST(Representational State Transfer) and RESTful API REST 是 Representational State Transfer 的縮寫,可譯為「表現層狀態轉換」。由 Roy Fielding 博士在 2000 年的博士論文中所提出。他同時也是 HTTP 規範的主要作者之一。符合 REST 風格的網站架構可以稱為 RESTful。 RESTful 是一種設計風格不是標準,目的是幫助網路之間交換訊息,每個網頁都是一個resource,透過URL交換資料。 ## 理解RestFul ### 資源與URL 任何會被引用到的事物都是資源,可以是實體(手機號碼)也可以是抽象概念(價值)。 * 在Web中唯一可以識別資源的標誌就是URI,沒有URI標誌就不算資源,只能算是資源的訊息。URL設計應遵循可尋找性、自描述性、可直覺聯想性,例如: https://github.com/git https://github.com/git/git https://github.com/git/git/blob/master/block-sha1/sha1.h https://github.com/git/git/commit/e3af72cdafab5993d18fae056f87e1d675913d08 https://github.com/git/git/pulls https://github.com/git/git/pulls?state=closed https://github.com/git/git/compare/master…next * 使用_或-提升URL可讀性 http://www.oschina.net/news/38119/oschina-translate-reward-plan。 * 使用/表示資源的層級,例如這是某次commit紀錄,或是/orders/2012/10表示2012年10月的commit。 /git/git/commit/e3af72cdafab5993d18fae056f87e1d675913d08 * 使用?來過過濾資源,例如/pulls?state=closed表示狀態已關閉的pull request * 用,或是;表示同級資源的關係,例如比較同一文件commit 兩次之間的差異/git/git /block-sha1/sha1.h/compare/e3af72cdafab5993d18fae056f87e1d675913d08;bd63e61bdf38e872d5215c07b264dcc16e4febca ### 統一資源請求 統一街口包含一組受限的的預定義請求,任何資源都過相同請求傳遞,標準HTTP方法有[GET, POST, PUT, DELETE]。這些請求都是安全(GET、HEAD)且冪等(GET、HEAD、PUT、DELETE),安全表示不管請求多少次都不會改變伺服器狀態。而冪等表示任一一次的請求結果都是一樣。 1. GET 安全且冪等 獲取資源 2. POST 不安全且不冪等 用server端(自動產生)的實例創建資源 部分更新資源 如無修改則不更新資源 3. PUT 不安全但冪等 用client端創建資源 如無修改也更新資源 4. DELETE 不安全但冪等 刪除資源 * POST和PUT區別在於創建的資源名稱(URI)是否由client決定。例如我要增加一個java分類,路徑就是/categories/java,那就可以用PUT。但http requests並不直接對應CRUD,只是rails框架就是這麼做因此容易造成誤解。 * 有些古老瀏覽器只支援GET、POST方法,因此需要一些方法調整。例如rails通過隱藏參數_method=DELETE來傳遞。而Backbone的MVC則允許_method傳輸和設置X-HTTP-Method-Override來規避這個問題。 * 統一請求並不限於那四種,只要對資源保有具體性核可識別的語意,並能保持請求統一性即可擴充。例如WebDAV增加了LOCK、UPLOCK。或是GitHub增加PATCH進行修改,不過PATCH不是標準HTTP請求。 * URL只表示資源名稱不應包含資源操作,URI不應該用請求來描述,下面是一些不符合统一接口要求的URI: GET /getUser/1 POST /createUser PUT /updateUser/1 DELETE /deleteUser/1 * 安全性不代表請求不產生副作用,例如像很多API開發平台,都對請求流量做限制。像github,就會限制沒有認證的請求每小時只能請求60次。但客戶端不是為了追求副作用而發出這些GET或HEAD請求的,產生副作用是服務端"自作主張"的。另外,服務端在設計時,也不應該讓副作用太大,因為客戶端認為這些請求是不會產生副作用的。 * 響應代碼的處理有必要嗎? 如上圖所示,HTTP的響應代碼可用於應付不同場合,正確使用這些狀態代碼意味著客戶端與服務器可以在一個具備較豐富語義的層次上進行溝通。例如,201(“Created”)響應代碼表明已經創建了一個新的資源,其URI在Location響應報頭里。假如你不利用HTTP狀態代碼豐富的應用語義,那麼你將錯失提高重用性、增強互操作性和提升松耦合性的機會。如果這些所謂的RESTFul應用必須通過響應實體才能給出錯誤信息,那麼SOAP就是這樣的了,它就能夠滿足了。 ### 資源的表述 client端透過HTTP方法取得資源,更確切的說只是資源的表述而不本身。資源的表述有很多種形式,client和server之間也是資源的表述而不是本身。資源表述方式包含html、xml、json等,圖片有PNG或JPG。資源表述包含數據和描述數據的元數據。 透過HTTP內容,client可以知道表述內容是哪一中格式,server則通過Content-Type告訴客戶端表述形式。例如GitHub就是用json表述形式。 下面我們來看一些實踐上常見的設計: 在URI裡邊帶上版本號 有些API在URI裡邊帶上版本號,例如: http://api.example.com/1.0/foo http://api.example.com/1.2/foo http://api.example.com/2.0/foo 如果我們把版本號理解成資源的不同表述形式的話,就應該只是用一個URL,並通過Accept頭部來區分,還是以github為例,它的Accept的完整格式是 application/vnd.github[.version].param[+json] 對於v3版本的話,就是Accept: application/vnd.github.v3。對於上面的例子,同理可以使用使用下面的頭部: Accept: vnd.example-com.foo+json; version=1.0 Accept: vnd.example-com.foo+json; version=1.2 Accept: vnd.example-com.foo+json; version=2.0 使用URI後綴來區分錶述格式 像rails框架,就支持使用/users.xml或/users.json來區分不同的格式。這樣的方式對於客戶端來說,無疑是更為直觀,但混淆了資源的名稱和資源的表述形式。我個人認為,還是應該優先使用內容協商來區分錶述格式。 * 如果遇到不支援的格式,應回覆HTTP 406狀態表示拒絕該請求 ### 資源的連結 * REST是使用標準HTTP方法操作資料,但不簡單是CRUD而已,其中有一個核心觀念是"超媒體即應用狀態引擎(hypermedia as the engine of application state)"超媒體是什麼?(https://zh.wikipedia.org/zh-tw/HATEOAS) 當你瀏覽Web網頁時,從一個連接跳到一個頁面,再從另一個連接跳到另外一個頁面,就是利用了超媒體的概念: 把一個個把資源鏈接起來. ### 資源的轉移 * 應用狀態與資源狀態 其實,這裡說的無狀態通信原則,並不是說客戶端應用不能有狀態,而是指服務端不應該保存客戶端狀態。實際上,狀態應該區分應用狀態和資源狀態,客戶端負責維護應用狀態,而服務端維護資源狀態。客戶端與服務端的交互必須是無狀態的,並在每一次請求中包含處理該請求所需的一切信息。服務端不需要在請求間保留應用狀態,只有在接受到實際請求的時候,服務端才會關注應用狀態。這種無狀態通信原則,使得服務端和中介能夠理解獨立的請求和響應。在多次請求中,同一客戶端也不再需要依賴於同一服務器,方便實現高可擴展和高可用性的服務端。但有時候我們會做出違反無狀態通信原則的設計,例如利用Cookie跟踪某個服務端會話狀態,常見的像J2EE裡邊的JSESSIONID。這意味著,瀏覽器隨各次請求發出去的Cookie是被用於構建會話狀態的。當然,如果Cookie保存的是一些服務器不依賴於會話狀態即可驗證的信息(比如認證令牌),這樣的Cookie也是符合REST原則的。 * 應用狀態的轉移 狀態轉移到這裡已經很好理解了, “會話”狀態不是作為資源狀態保存在服務端的,而是被客戶端作為應用狀態進行跟踪的。客戶端應用狀態在服務端提供的超媒體的指引下發生變遷。服務端通過超媒體告訴客戶端當前狀態有哪些後續狀態可以進入。這些類似“下一頁”之類的鏈接起的就是這種推進狀態的作用–指引你如何從當前狀態進入下一個可能的狀態。 ### 你的REST不是REST? https://www.ithome.com.tw/voice/128528 ## RESTful API特性 * ***無狀態(Stateless)***:每一個請求都是獨立的,服務器不會儲存任何請求的狀態。 * ***客戶端-服務器(Client-Server)***:客戶端負責用戶交互,服務器負責存儲和處理資料。兩者相互獨立,方便進行分開維護或擴展。 * ***可快取(Cacheable)***:響應中的資料能夠被客戶端快取,提高性能。 * ***分層系統(Layered System)***:可以使用代理伺服器進行轉發,或提供安全性控制等。 * ***統一介面(Uniform Interface)***:API 的設計要盡可能簡單且一致。 RESTful 風格的網址設計強調從路由結構就能看出要對什麼資料、進行什麼操作。舉例來說,如果資料是 todos,那麼 RESTful 風格的 CRUD 路由就會這樣寫:![](https://hackmd.io/_uploads/HyKINUDnh.png) 從網址上,你會看出要操作的資源叫做 todos,這邊習慣用複數名詞。然後固定結構是: * 瀏覽全部資料:GET + 資源名稱 * 瀏覽特定資料:GET + 資源名稱 + :id * 新增一筆資料:POST + 資源名稱 * 修改特定資料:PUT + 資源名稱 + :id * 刪除特定資料:DELETE + 資源名稱 + :id ## HTTP 狀態碼 1. 資訊回應 (Informational responses, 100–199) 2. 成功回應 (Successful responses, 200–299) 3. 重定向 (Redirects, 300–399) 4. 用戶端錯誤 (Client errors, 400–499) 5. 伺服器端錯誤 (Server errors, 500–599 https://zh.wikipedia.org/zh-tw/HTTP%E7%8A%B6%E6%80%81%E7%A0%81 Q: 403 vs 401, dochi? A: 401 許多服務需要先知道使用者是誰,再來判斷是否有權限,當連使用者是誰都不知道時(未登入),就是 401。而 403 則是『這個使用者沒有權限做這件事情』,通常是在登入後才會有的狀態,但如果你不希望使用者知道他沒有這個權限,404 也是個選擇。 Q: 400 vs 422 A: 400 代表使用者端傳送了錯誤的格式,422 則是格式正確但伺服器無法處理內容,可能是商業邏輯的驗證失敗,例如 Rails 框架會把失敗的 model validation 以 422 來處理。 Q: 402 Payment Required A: 尚未有完整定義,目前被用來處理與付費相關的狀態: 1. 在付費服務中,你還沒付錢或錢給得不夠 (quota 到了)所以這個功能不能使用 2. 金流服務中的付費流程沒有走完整 Q: 200 還是 204? A: 204 常見用在物品的移除,沒有後續資訊可提供的狀態下。如果有可能有其他資訊時還是要用 200