到底該怎麼串接API?

金流💵、物流🚚、天氣🌤️、新聞📰...這麼多方便的 API 到底該怎麼串接? 這篇將以現實生活中的情境作為範例,幫助你快速、輕鬆的認識並開始串接 API!

到底怎麼串接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 Copy
fetch('/example'); // GET 為預設行為
fetch('/example', {
  method: '...' // 依據自身需求調整
});

這樣一來便可以成功向 API 發起請求,但是成功發起請求並不代表執行成功,大多數情況下還需要根據需求調整接下來幾章中介紹的項目。

請求標頭 Headers

用以告知 API 此次請求的資訊,如:表單格式、憑證、來源...等。

格式 Content-Type

雖然發起請求很簡單,但就像實際生活中存在各種不同的文件格式一樣,請求 API 的格式也有很多種。
最常使用的兩種請求格式

  • multipart/form-data - 可攜帶文字、檔案
  • application/json - 僅允許json字串(純文字)

當然格式並不只有這兩種,可以參考 IANA - Media Types 認識所有格式。

javascript Copy
fetch('/example', {
  headers: {
    'Content-Type': 'application/json' // 依據自身需求調整
  }
});

憑證 Authorization

那麼如果請求的服務需要權限或身份驗證,櫃台人員該怎麼判斷是哪位用戶?能不能操作?
這些都是透過攜帶憑證 Authroization 來判斷的,但為什麼需要攜帶憑證?直接寫在請求內容不就好了嗎?
沒錯!確實可以這麼做,登入通常就是這麼一回事,但每次都要求用戶填寫帳號、密碼會讓體感相當糟糕!
所以系統通常會在用戶登入後配發唯一、不重複的憑證,用戶只須在執行請求時帶上就好了。
可以理解為進入機構後取得了一組專屬的「服務編號」,櫃台人員可以透過編號確認用戶身份、查詢相關資料。

javascript Copy
fetch('/example', {
  Authorization: '...' // 填入專屬憑證
});

格式一樣,憑證的類型及攜帶方式也不是只有一種,在此就不多做贅述,之後再另外寫一篇來討論 (自己挖坑)

請求內容 Request Body

選好格式、帶上憑證後,當然不能漏了最重要的請求內容,基本上就是依據 API 文件要求的填寫內容即可。

javascript Copy
fetch('/example', {
  body: JSON.stringify({
    title: '標題',
    description: '簡述',
    content: '內容',
  })
});

這裡以非常簡易的「新增文章」作為示範,一切皆須以 API 文件為準。

跨來源資源共用 CORS

所以...完成以上設置就能成功串接 API 了嗎?通常是的,但在瀏覽器上操作通常還會碰到 CORS 同源限制。
CORS 相當於守衛,只有透過瀏覽器發起請求才會發生,當請求的網域與 API 網域不相同,收到回應便會被 CORS 屏蔽,只有在協定(protocol)、網域(domain)、埠號(port)完全一致時才能順利取得回應。
為什麼瀏覽器要這樣限制?這是為了避免惡意網站透過 Javascript 取得用戶瀏覽器上的資料並假冒用戶發起請求,但這僅僅是保護機制的一環,並不代表有它就萬無一失了。
那麼該如何解決 CORS 限制?

  1. 透過 Proxy 代理轉發請求(如 CORS anywhere
  2. 由當前網域的後端發起請求(如 webpack-dev-server, nitro)
  3. 請後端將前端網域加入白名單(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 Copy
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 請求就是這麼簡單的一件事,讓我們重新把整個流程以現實生活情境描述一次。
情境:用戶想要在某個單位發布文章

  1. 進入單位 - 登入及取得憑證
  2. 選擇文件類型 - 格式 Content-Type
  3. 填寫文件內容 - 請求內容 Request Body
  4. 攜帶服務編號 - 憑證 Authorization
  5. 送出表單等待回應 - 狀態碼 Status Code, 回應內容 Response Body

最後編輯:2025-01-17 13:03:15