【Spring Boot】第2課-認識 RESTful API 與前後端的資料交換 #2024 年更新


https://unsplash.com/photos/LmyPLbbUWhA

在認識前端與後端的差別後,本文將介紹它們之間的溝通媒介,即 Web API,其中又以 RESTful API 的類型最為常見。另外也會介紹查詢字串、負載(payload)、標頭及 HTTP 狀態碼,這些都是前後端所交換的資料。最後透過 Postman 這項工具,存取實際的 API。

若讀者未曾接觸後端領域,建議先從本文建立 Web API 的觀念。如此在第 3 課開始撰寫 Spring Boot 程式,以及工作時設計 API 時,才能更加得心應手。

一、什麼是 API

API 的全名是「Application Program Interface」,中文為「應用程式介面」,它定義了軟體之間的互動方式。

從廣義的角度來看,即便只是程式中寫好的一個方法(method),我們都能視為一個 API。畢竟它定義了應傳入的參數,以及回傳的值,讓我們知道如何使用。

以生活情境來比喻,我們到餐廳點餐,菜單就像是 API。雖然每家餐廳的菜單格式都長得不一樣,但只要客人會填寫,而店員或廚房也看得懂客人的點餐,整個流程便能正常進行。

如果放在前端發出請求(request),後端給予回應(response)的話題上,則 API 就是定義前端如何傳遞資料給後端,而後端又會回傳什麼結果。我們用「Web API」來稱呼。

在工作上,前端與後端的同事只要事先制定好 API,就能分頭在各自的程式專案進行開發了,雙方的程式碼也不會混在一起形成雜亂。這種做法稱為「前後端分離」,是業界工程師們推崇的模式。


二、RESTful API

REST 的全名為「Representational State Transfer」,中文為「表現層狀態轉換」。它是一種 Web API 的設計風格,將網路上的東西都視為「資源」,並且有不同的操作方式。

(一)HTTP 請求方法

當使用者發出請求,意味著要對某種東西進行操作,比方說取得產品或新增使用者。操作的方式包含新增、取得、更新、刪除等,如同資料庫也有 CRUD。

HTTP 協定以這個概念為基礎,定義了「請求方法」,常見的方法如下。

  • GET:取得資源。
  • POST:新增資源。或是一個不容易歸類在其他方法的動作,例如登入。
  • PUT:更新整個資源,也就是覆寫。
  • PATCH:更新資源的一部份。
  • DELETE:刪除資源。

(二)表示方式

RESTful API 是以「動詞 + 資源」這個想法為出發點,也就是 HTTP 請求方法加上資源的位置。而資源位置的表達方式有點像網址,我們以「端點」(endpoint)來稱呼它。

假設社群網站上有篇文章的編號是「P135」,那麼這項資源在網路上的位置,可能就會被設計成「https://social.com/articles/P135」。此時搭配各種 HTTP 請求方法,即可組合出 RESTful API,表現出不同的意義。以下是一些範例。

  • GET /articles/P135:取得編號為 P135 的文章。
  • PUT /articles/P135:更新編號為 P135 的文章。
  • DELETE /articles/P135:刪除編號為 P135 的文章。
  • POST /articles:新增文章。
  • GET /articles:取得所有文章。

其中「新增文章」的 API 是沒有編號的。從語意上,已經足夠表達出要在「articles」的路徑下新增資源了。

同理,「取得所有文章」的 API 也意味著取得 articles 路徑下的所有資源。但實際開發上通常不會真的回傳 DB 中的所有資料給前端啦,應該會限制一個範圍才對。例如某個人的所有文章、某個分類的所有文章、最新的 30 篇文章等。

最後要提醒讀者的是,RESTful 並非一套要遵守的標準。只是鼓勵開發者在設計時可以參考此風格,縮短大家認知上的差異。


三、在 API 攜帶資料

前端在呼叫 API 時,可以攜帶資料給後端,而後端同樣也能回傳資料,就像在交換一樣。

以上述的 RESTful API 為例子,新增文章時勢必要傳遞內容給後端吧!又或者是想查看某人的文章,也要將這項條件告訴後端。而取得文章時,則從後端得到對應的資料。本節將介紹攜帶資料的方式。

(一)查詢字串(query string)

查詢字串是呼叫後端 API 時,在端點後方所攜帶的資料。舉例來說,在論壇 Dcard 查看科技業板的最新文章之列表,瀏覽器的網址是這樣:
https://www.dcard.tw/f/tech_job?tab=latest」。

其中「?」符號後方的就是查詢字串,它只有「tab=latest」一筆資料,代表最新的意思。

又或者是在 YouTube 觀看一部影片,瀏覽器的網址大概長這樣:
https://www.youtube.com/watch?v=u6FcPuCs57o&=80s」。

其包含「v=u6FcPuCs57o」與「t=80s」兩筆資料,分別代表影片編號與開始播放的秒數。它們之間用「&」符號隔開。

(二)負載/酬載(payload)

負載(又稱酬載)在維基百科的解釋是:「資料傳輸中所欲傳輸的實際資訊,通常也被稱作實際資料或者資料體」。在本文,我們可以理解成前後端之間傳遞資料的「主要內容」。

前端發出請求時的 payload,亦可稱作「request body」;而後端給予回應時的 payload,也能稱為「response body」。

那麼所要傳遞的資料,應該如何提供呢?用 JSON 和 XML 格式來表示,是較為常見的做法。筆者會以 JSON 為例來說明。

JSON 的英文全名是「JavaScript Object Notation」,是將資料寫成 Java Script 物件的樣子。以下是一份 JSON 格式的範例資料。

{
    "title": "這是文章標題",
    "content": "這是文章內容",
    "tag": ["閒聊", "程式"],
    "like": {
        "isLiked": true,
    	"count": 28
    }
}

JSON 資料的內容,都是以欄位: 值的格式來撰寫。而物件寫成大括弧({ }),陣列寫成中括弧([ ])。

(三)標頭(header)

標頭是用來存放一些輔助資訊。以生活情境來比喻,header 彷彿信封袋上寫的資料,而 payload 則是信件內容。在 header 所能提供的資料,是有一套標準存在的。就像信封袋會寫郵遞區號、地址和收件人。

根據 header 是由前端還是後端所提供,又可分別稱呼為「request header」與「response header」。

以下舉例幾種 header。

  • Content-Type:Payload 內容的格式。如 application/json、image/jpeg、text/html、multipart/form-data 等。
  • Location:資源的端點。比方說建立新資源後,後端可回傳此標頭,提供該資源的路徑。
  • Authorization:身份認證資訊。向後端發出請求時可攜帶,就像是出示識別證一樣。
  • X-Forwarded-For:這份資料所經過的 IP 位址。

四、HTTP 狀態碼

當後端進行回應時,會用一個狀態碼來「概略」表達結果。讓前端能夠簡單地從狀態碼,判斷自己下一步該做什麼事,而未必要去取用 payload 的內容。

狀態碼是由 3 個數字組成,其中第一個數字會在 1~5 之間,藉此分成五大類型。後端會在不同的狀況,使用不同的狀態碼。除了 1 系列幾乎沒人在用之外,筆者挑出一些常見的,在以下分別介紹。

(一)2 系列(成功)

  • 200(OK):單純表示成功,是一種泛用的選擇。
  • 201(Created):資源已經建立完成。後端可進一步在「Location」標頭提供該資源的端點,讓前端搭配 GET 請求方法取得該資料。
  • 204(No Content):伺服器成功處理請求,只是回傳的 payload 沒有內容罷了。例如「按讚」這項簡單的功能,若 API 不需要回傳 payload,則不妨採用此狀態碼。

(二)3 系列(重導向)

  • 301(Moved Permanently):該資源已經永久被移動到另一個地方。建議後端在「Location」標頭提供該資源的新端點。會用到此狀態的場合,可能是網頁搬家,或是端點的設計更新了(例如網址上多了一個「v2」的階層,代表版本 2)。
  • 304(Not Modified):用於快取機制。前端先前有將該資源快取在本地,若嘗試再獲取資源,後端可透過此狀態,告知這段期間並未更新過。此時前端繼續用快取的資料即可,同時也省下傳輸成本。該機制需搭配數個 header 來實作,本文不贅述。

(三)4 系列(客戶端錯誤)

代表前端發送的資料有問題。

  • 400(Bad Request):發送請求時所攜帶的資料有明顯錯誤。例如 email 格式不正確、價格是負數等。
  • 401(Unauthorized):沒有通過身份認證,所以後端不允許使用該 API。例如沒有登入某社群網站,那就無法查看裡面的任何文章。
  • 403(Forbidden):即便完成身份認證,但沒有權限存取該資源。例如已經登入某社群網站,但我不是某篇文章的分享對象,所以無法觀看。
  • 404(Not Found):該資源不存在。例如試圖透過編號取得一篇不存在的文章。
  • 422(Unprocessable Entity):前端發送請求,單看資料內容的話是沒問題(未被判為 400),但後端處理時才發現這份資料並不符合情境。例如下單購買商品時,前端發送「以歐元付款」,結果後端發現賣方不接受這個幣別(參考資料)。

(四)5 系列(服務端錯誤)

代表後端在處理請求時發生問題。

  • 500(Internal Server Error):後端程式可能發生例外(exception),例如 NullPointerException 或是連不到資料庫之類的。若無更適當的狀態碼,一般狀況均可選擇這種泛用的。
  • 503(Service Unavailable):代表雖然後端程式仍在運行,但因為一些原因(例如流量太大、正在維護),暫時無法提供服務了。
  • 504(Gateway Timeout):後端處理請求的時間過久。然而前端一直在等待回應,不能丟著不管,故可回傳此狀態碼進行告知。

五、API 測試工具

最後介紹一款叫做「Postman」的工具,它能讓我們在沒有前端的情況下,存取後端提供的 Web API。

一般來說,生活中使用各種軟體都是透過操作前端畫面來存取後端 API。但有時只是想「試用」API,當下並沒有對應的前端去配合。那麼利用這類工具,我們就能發送請求,傳送自己想要的資料,藉此測試 API 的效果。

以下兩個操作範例,是「Reqres」網站免費提供的 API 測試資料。不需註冊,讀者直接使用即可。

下圖是發出 GET 請求,並搭配查詢字串。

下圖是發出 POST 請求,並傳送 JSON 格式的 payload。

從操作畫面的右方,也能看見耗時與 HTTP 狀態碼。將「Body」的下拉式選單打開,還能切換查看 response header。

上一篇:【Spring Boot】第1課-從環境準備、建立專案、打包到啟動程式

下一篇:【Spring Boot】第3課-在 Controller 實作 RESTful API

留言