到底怎麼串接API?
串接 API 是系統中非常重要的一環,透過不同的 API 可以讓系統擴充各種由第三方提供的服務、資料,例如政府資料開放平台就提供了非常多的公開資訊開放請求,當然不是所有 API 都免費提供串接,例如金流或物流等專業服務就需要申請、付費才能使用。
前言
不同語言的作法皆有所差異,接下來僅一律以 Javascript 及 RESTful 風格,並以「前往某機構申請資料或服務」的情境作為範例。
快速連結
資源路徑 Resource Path
指的是開放請求、串接的項目,通常也會明確列出該以什麼請求方式進行請求。
請求方式 | 資源路徑 | 提供服務 |
---|---|---|
GET | /articles | 讀取文章列表 |
GET | /articles/{id} | 讀取單一文章 |
POST | /articles | 新增文章 |
PUT | /articles/{id} | 編輯單一文章 |
DELETE | /articles/{id} | 刪除單一文章 |
RESTful 風格的 API 主要以請求方式區分行為目的,所以資源路徑看起來幾乎相同是正常的。
請求方式 Method
當我們透過不同的方式發起請求,櫃台人員便能了解此次的行為目的。
常見請求方式
- GET - 讀取
- POST - 新增
- PUT / PATCH - 編輯
- DELETE - 刪除
簡稱 CRUD,代表 Create(C), Read(R), Update(U), Delete(D)。
javascript
fetch('/example'); // GET 為預設行為
fetch('/example', {
method: '...' // 依據自身需求調整
});
這樣一來便可以成功向 API 發起請求,但是成功發起請求並不代表執行成功,大多數情況下還需要根據需求調整接下來幾章中介紹的項目。
請求標頭 Headers
用以告知 API 此次請求的資訊,如:表單格式、憑證、來源...等。
格式 Content-Type
雖然發起請求很簡單,但就像實際生活中存在各種不同的文件格式一樣,請求 API 的格式也有很多種。
最常使用的兩種請求格式
- multipart/form-data - 可攜帶文字、檔案
- application/json - 僅允許json字串(純文字)
當然格式並不只有這兩種,可以參考 IANA - Media Types 認識所有格式。
javascript
fetch('/example', {
headers: {
'Content-Type': 'application/json' // 依據自身需求調整
}
});
憑證 Authorization
那麼如果請求的服務需要權限或身份驗證,櫃台人員該怎麼判斷是哪位用戶?能不能操作?
這些都是透過攜帶憑證 Authroization 來判斷的,但為什麼需要攜帶憑證?直接寫在請求內容不就好了嗎?
沒錯!確實可以這麼做,登入通常就是這麼一回事,但每次都要求用戶填寫帳號、密碼會讓體感相當糟糕!
所以系統通常會在用戶登入後配發唯一、不重複的憑證,用戶只須在執行請求時帶上就好了。
可以理解為進入機構後取得了一組專屬的「服務編號」,櫃台人員可以透過編號確認用戶身份、查詢相關資料。
javascript
fetch('/example', {
Authorization: '...' // 填入專屬憑證
});
和格式一樣,憑證的類型及攜帶方式也不是只有一種,在此就不多做贅述,之後再另外寫一篇來討論
(自己挖坑)。
請求內容 Request Body
選好格式、帶上憑證後,當然不能漏了最重要的請求內容,基本上就是依據 API 文件要求的填寫內容即可。
javascript
fetch('/example', {
body: JSON.stringify({
title: '標題',
description: '簡述',
content: '內容',
})
});
這裡以非常簡易的「新增文章」作為示範,一切皆須以 API 文件為準。
跨來源資源共用 CORS
所以...完成以上設置就能成功串接 API 了嗎?通常是的,但在瀏覽器上操作通常還會碰到 CORS 同源限制。
CORS 相當於守衛,只有透過瀏覽器發起請求才會發生,當請求的網域與 API 網域不相同,收到回應便會被 CORS 屏蔽,只有在協定(protocol)、網域(domain)、埠號(port)完全一致時才能順利取得回應。
為什麼瀏覽器要這樣限制?這是為了避免惡意網站透過 Javascript 取得用戶瀏覽器上的資料並假冒用戶發起請求,但這僅僅是保護機制的一環,並不代表有它就萬無一失了。
那麼該如何解決 CORS 限制?
- 透過 Proxy 代理轉發請求(如 CORS anywhere)
- 由當前網域的後端發起請求(如 webpack-dev-server, nitro)
- 請後端將前端網域加入白名單(Access-Control-Allow-Origin)
前兩點基本上是一樣的東西,透過允許當前網域請求或 Access-Control-Allow-Origin:* 並且存在代理機制的後端,將請求轉發至欲請求的 API。
總而言之,CORS 無法單純靠前端獨立解決,需由後端調整設置。
警告
Proxy 代理存在一定的風險,資料可能被代理方擷取、利用,須謹慎使用!
回應 Response
成功送出請求後 API 便會將請求的結果返回給我們。
狀態碼 Status Code
狀態碼能夠幫助我們快速的了解請求的結果,根據 wiki 上的描述共分為五個種類
- 1xx - 資訊回應,表示正在處理中
- 2xx - 成功,表示操作成功
- 3xx - 重新導向,表示用戶需進一步操作
- 4xx - 客戶端錯誤,表示操作失敗
- 5xx - 伺服器錯誤,表示異常、無法處理
詳細代碼及含意請參考 IANA - Status Code
回應內容 Response Body
但許多情境中只有狀態碼是行不通的,還需要回應內容才算是完整的流程。
行為 | 狀態碼 | 回應內容 |
---|---|---|
GET 文章列表 | 200 | 文章列表 |
GET 單一文章 | 200 | 單一文章 |
POST 新增文章 | 400 | 失敗原因 |
DELETE 歷史文章 | 400 | 失敗原因 |
API 的回應內容結構不一定相同,一切皆須以 API 文件為準。
其他
完整範例
如果你已經完全理解了前面提到的內容,那麼聰明的你肯定不需要說明就能明白這段範例的作用了👍
javascript
fetch('/articles', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: '...', // 填入專屬憑證
},
body: JSON.stringify({
title: '標題',
description: '簡述',
content: '這是一篇文章',
})
})
.then(response => {
console.log('文章新增成功!可以透過 response 或瀏覽器工具查看詳細回應');
})
.catch(error => {
console.log('文章新增失敗!可以透過 error 或瀏覽器工具查看詳細錯誤資訊');
})
.finally(() => {
console.log('API 請求流程結束!');
});
工具
這邊分享一些可以協助開發、除錯的請求工具,它們通常包含自動化測試、情境示例建置、生成隨機值填充內容...等強大功能。
結論
基本上 API 請求就是這麼簡單的一件事,讓我們重新把整個流程以現實生活情境描述一次。
情境:用戶想要在某個單位發布文章
- 進入單位 - 登入及取得憑證
- 選擇文件類型 - 格式 Content-Type
- 填寫文件內容 - 請求內容 Request Body
- 攜帶服務編號 - 憑證 Authorization
- 送出表單等待回應 - 狀態碼 Status Code, 回應內容 Response Body
最後編輯:2025-01-17 13:03:15