iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 26
2
Modern Web

前端三十 - 成為更好的前端工程師系列 第 26

26. [BE] API 設計拿資料要透過 POST,會有什麼問題嗎?

restful

現代的網站越來越多前後端分離的架構,透過 前端 MVC 框架 快速堆砌出 SPA,再透過 API 取得變動的資料也已經是開發者日常;而用來聯繫前後端的 API,其重要性自然也就不言而喻了。身為一個前端開發者,認識後端的 API 設計方式也是很重要的一環,今天就讓我們針對 API 設計來一探究竟吧!

本系列文已經重新編校彙整編輯成冊,並正式出版囉!
《前端三十:從 HTML 到瀏覽器渲染的前端開發者必備心法》好評販售中!
喜歡我文章內容的讀者們,歡迎您 前往購買 支持!

HTTP Method

畢竟是網站的前後端,其中間的通訊,終究還是要仰賴 HTTP 這個無狀態的協定;在 HTTP 規範中 有定義了一系列的 Request Method,其中較常用到的如下:

  • GET:取得資源
  • HEAD:與 GET 同為取得資源,但只取回 Header
  • POST:提交資源
  • PUT:取代指定資源
  • PATCH:修改指定資源
  • DELETE:刪除指定資源
  • OPTION:詢問與指定資源的溝通方法

在規範中也提到,不同的 Method 指的是對同一件事情做不同的操作,並透過語意化的(semantic)Methods,讓不同的操作結果得以被預期。

GETPOST 相信絕大多數的開發者都不陌生,這是 HTML 的 <form action="..."> 唯二支援的 Methods;GET 是最頻繁使用的,無論是取得頁面、資料,一般而言都會使用GETPOST 則常用在新增資源,但由於前述的 HTML <form action="..."> 不支援其他 Methods,在傳統網站中可能會用 POST 處裡除了取得資料以外的所有事情。

關於為什麼 HTML 不支援其他 HTTP Methods,可以參考 這則問答,及 這則 Issue 討論

PUTPATCH 通常都用在更新資源,兩者的差異是 PUT 預期的行為會是取代整個資源,而 PATCH 則是更新部分資源;把兩者對應到生活化案例的話,例如在餐廳吃飯,整桌重新點菜是 PUT,另外加點則會是 PATCH

DELETE 通常用在刪除資源;HEADGET 同樣為取回資源,但只取回 Header,通常會用在測試資源是否存在;OPTION 是詢問這個資源應該要怎麼獲取,常在 發送 CORS 的預檢(preflight)請求 時使用。

當然,目前講的都是規範提到且較建議的一般用法,實際伺服器的 API 怎麼開依然是看實作者;但透過語意化的 Methods 設計 API,絕對可以讓 API 對開發者更加友善。

RESTFul API

前述的只是規範,而且只圍繞在 HTTP Methods 上;有沒有更完整的實作方法建議呢?

當然有囉,就是大家耳熟能詳的 RESTFul API;由 Roy Fieldin 在 IEFT 開發 HTTP 標準的六年間持續專研、驗證,並在 2000年 於博士論文 《Architectural Styles and the Design of Network-based Software Architectures》 中所提出,是一種網路程式的設計風格;這邊的 REST 是 表現層狀態轉換(Resource Representational State Transfer) 的縮寫,簡單說就是透過動詞(HTTP Methods)、名詞(URI/URL,代表目標資源)、內容型態(回應的內容,HTML、XML、JSON、etc.),讓無狀態的網路通訊能藉由 REST 的語意化設計,攜帶所有的狀態資訊,降低對網路通訊的重複請求資源消耗。

例如,有個假想的影音網站:mytube.com,開出來的 API 有可能就會長這樣子:

[GET] http://mytube.com/v1/videos/ -> 取得 video 列表
[POST] http://mytube.com/v1/videos/ -> 新增 video
[GET] http://mytube.com/v1/videos/MgphHyGgeQU -> 取得指定 ID 的 video
[PUT] http://mytube.com/v1/videos/MgphHyGgeQU -> 修改指定 ID 的 video
[DELETE] http://mytube.com/v1/videos/MgphHyGgeQU -> 刪除指定 ID 的 video

除了使用的方法之外,也要注意代表資源的 URL 的撰寫方式,不是 HTTP Methods 與實際動作相符合就是 RESTful API 囉!

同樣的,RESTFul API 只是設計風格,甚至不是 HTTP 的規範,很有可能設計時基於 RESTful 的精神,但實際開發的結果全然不是 RESTful 的風格;但不可否認的是,透過 RESTful API 的設計風格,每個資源都會得到一個到對應的位置(URL),並能透過 HTTP 語意化的方法,對指定的資源做相對應的互動,整體資源管理便能非常語意化且清晰,確實是一個優秀的 API 設計方式。

規範與實作

我們在 討論瀏覽器差異時,便有提到規範與實作的差異太大時,會發生什麼事情;而 HTTP 的 Methods 在規範中也有提到要如何正確使用,如果開發者沒有依照規範,確實是會造成影響的。

快取

例如 幾天前聊過的網頁快取,瀏覽器預設行為會對 GETHEAD 這兩個方法做快取,如果像標題說的取資料不是透過 GET 而是 POST,瀏覽器及中間的代理伺服器都很可能都不會實作快取機制,就必須由前後端開發者自行透過其他方式設置快取。

在規範中雖然有提到 POST 在 Header 合適的情況下也可以快取,但由於實務上通常把 POST 用在新增,快取起來反而會造成不預期的結果,大部分瀏覽器也都沒有實作 POST 的快取機制。

SEO

先前也有聊到 SEO 的議題,搜尋引擎例如 Google 的爬蟲 在掃網站時,如果看到需要透過 POST 取得的資源,為了避免造成意外的行為或副作用(改到資料),通常不會嘗試爬找 POST 回應的結果。

GraphQL

RESTful API 的設計風格優點不少,但有一些缺點也是難以避免。

例如在查找有依賴關係的巢狀資料時,很有可能必須要經過多次來回,才能找到想要的結果;而隨著專案架構逐漸擴張,同一頁面的資料也會越來越複雜,可能需要多個來源的資料才能堆砌出頁面,這時候 RESTful API 指名每個資源位置的特性,就會讓 RESTful API 顯得不太好用;也因為現在行動裝置非常普遍,一個後端伺服器可能需要服務電腦版網頁、手機 APP 等多裝置的需求,需要的資料可能不一樣,RESTful API 也就必須要開出多個功能類似的接口,整體的 API 便會量多而龐雜,相對難以管理。

有這樣的需求,就會有解決需求的人;GraphQL 就應運而生了,這是一個由 Facebook 提出的開源語言標準,透過 Schema 定義資料,再透過與 JSON 格式高度類似的查詢語句,取得查詢的結果,主要有幾個特色:

  • 強型別
  • 查詢句即文件
  • 查詢句即回應的資料結構,不會有冗餘的內容
  • 統一的對外入口
  • 可拼接查詢,一併送出

這些特性,有效的解決了前述 RESTful API 在複雜架構下的問題,使得 GraphQL 充滿彈性、非常好用,社群也已經有可觀的生態系支援,例如 Apollo GraphQL 與三大框架深度整合,配上可拼接查詢的特性,便能讓 GraphQL 與現代框架 Components 的概念完美契合。

缺點大概就是必須把所有複雜的資料串接邏輯都寫在後端,對於寫習慣 RESTful API 的開發者來說,需要付出不少學習成本。

值得注意的是,GraphQL 送出的請求全部都會是 POST,快取的機制必須仰賴開發者或是套件實現;例如在 Apollo Client 中,開發者必須依照應用情境,調整 fetchPolicy 的設定,避免快取造成的意外結果。

結語

標題是一位朋友去面試某公司後端工程師時被問到的題目;由這個題目出發,我們依序理解了 HTTP Methods 及主流的 RESTful API 設計風格,並對 GraphQL 做了簡短的介紹,希望以上內容能幫助到讀者您。

本文就到這邊啦,如果文中有任何錯誤,或是說明不清楚的地方,都歡迎您於底下留言回應喔!

參考資料

筆者

Gary

半路出家網站工程師;半生熟的前端加上一點點的後端。
喜歡音樂,喜歡學習、分享,也喜歡當個遊戲宅。

相信一切安排都是最好的路。


上一篇
25. [BE] Node.js 與 JavaScript 的關係是什麼?
下一篇
27. [WEB] Cookie & Session 是什麼?
系列文
前端三十 - 成為更好的前端工程師31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

1
huli
iT邦新手 3 級 ‧ 2019-10-12 18:06:03

對於舉例的那一段有點小疑問:

[GET] http://mytube.com/v1/video/ -> 取得 video 列表
[POST] http://mytube.com/v1/video/MgphHyGgeQU -> 新增 video
[GET] http://mytube.com/v1/video/MgphHyGgeQU -> 取得指定 ID 的 video
[PUT] http://mytube.com/v1/video/MgphHyGgeQU -> 修改指定 ID 的 video
[DELETE] http://mytube.com/v1/video/MgphHyGgeQU -> 刪除指定 ID 的 video

先講一下我沒有看原始論文,所以不確定是不是原始論文就是這樣寫還是寫錯XD

  1. 依照 RESTful 的慣例,資源名稱應該會是複數,所以 /videos 應該比較合理?
  2. 新增 video 那邊,應該是 [POST] http://mytube.com/v1/video/
Gary iT邦新手 5 級 ‧ 2019-10-12 18:28:10 檢舉

你是對的 我寫的時候沒改好XD

感謝糾正/images/emoticon/emoticon41.gif

1
hannahpun
iT邦新手 4 級 ‧ 2019-10-17 04:40:16

感謝分享 內容幾乎是面試必考題了阿! 以前都只會參考 API 文件接都沒好好想過為什麼用 put 或 patch

Gary iT邦新手 5 級 ‧ 2019-10-17 09:55:02 檢舉

哈哈~我以前是只會用 GET & POST,而且還全部都導去同一個函式處理;後來換工作才知道原來還有其他方法 XD

謝謝支持/images/emoticon/emoticon41.gif

我要留言

立即登入留言