axios 的 API 管理方法

在開發 Web 應用程式時,axios.create() 是一個非常強大且常用的功能。簡單來說,它允許你建立一個 Axios 的自定義實體 (Instance)

為什麼要這樣做呢?因為在大型專案中,你可能需要連接不同的 API 伺服器,或者希望為所有的請求設定統一的標準(例如:超時時間、特定的 Header 或基礎路徑)。


1. 基本語法

你可以透過傳遞一個配置物件(config)來建立實體:

const apiClient = axios.create({
  baseURL: '<https://api.example.com>',
  timeout: 5000, // 超過 5 秒則判定為失敗
  headers: {'X-Custom-Header': 'foobar'}
});

建立之後,你就可以像使用原本的 axios 一樣使用 apiClient

apiClient.get('/users')
  .then(response => console.log(response.data));
// 實際請求路徑為:<https://api.example.com/users>

2. 為什麼要使用axios.create()?

使用實體化主要有以下三個好處:

A. 統一管理配置 (Configuration)

如果你的 API 都在同一個域名下,你不需要在每個請求都寫完整的 URL。只需設定一次 baseURL

B. 區分不同的 API 來源

如果你的專案同時需要呼叫 Google API 和 自家的後端 API,你可以建立兩個不同的實體:

  • googleDriveClient: 設定 Google 的驗證與路徑。
  • internalServerClient: 設定自家伺服器的路徑與超時規則。

C. 獨立的攔截器 (Interceptors)

這是最重要的功能。你可以為不同的實體設定不同的請求攔截器回應攔截器。例如:在「後端實體」中統一加入 JWT Token,而在「外部 API 實體」中則不加入。


3. 實戰範例:封裝 API 模組

在開發中,我們通常會將 API 實體獨立成一個檔案(如 request.js):

// utils/request.js
import axios from 'axios';

const service = axios.create({
  baseURL: process.env.VITE_API_URL, // 從環境變數讀取路徑
  timeout: 10000
});

// 請求攔截器:在發送請求前做些什麼(例如加上 Token)
service.interceptors.request.use(
  config => {
    const token = localStorage.getItem('token');
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`;
    }
    return config;
  },
  error => Promise.reject(error)
);

// 回應攔截器:統一處理錯誤訊息
service.interceptors.response.use(
  response => response.data, // 直接回傳 data 內容,簡化後續呼叫
  error => {
    alert('發生錯誤:' + error.response.status);
    return Promise.reject(error);
  }
);

export default service;

4. 常用配置項一覽

配置項說明範例
baseURL請求的基礎路徑,會自動拼接到 URL 前面'<https://api.site.com>'
timeout設定請求超時(毫秒)5000
headers自定義的請求頭{'Content-Type': 'application/json'}
withCredentials跨域請求時是否攜帶 Cookietrue / false

5. 實戰應用

一個專案裡面不會只使用 1、2 支API,如果我們不希望這些 API 的路徑或是方法散落在專案各處,我們可以使用以下方法統一管理

// api/http.js
import axios from "axios";
// 引入 Authorization
import requestHelper from "@/utils/requestHelper";

const http = axios.create({
  baseURL: privateDomain, // 共用的統一路徑
  timeout: 15000,
});

// request 攔截
http.interceptors.request.use(
  (config) => {
    config.headers = {
      ...config.headers,
      ...requestHelper.setAuthorizationHeader(),
    };
    return config;
  },
  (error) => Promise.reject(error)
);

// response 攔截
http.interceptors.response.use(
  (response) => response.data, // 統一只回 data
  (error) => requestHelper.handleError(error)
);

export default http;
 

打 API 的方式改為函式統一管理:

// api/request.js
import http from "./http";

// 第一支 API
export const getFirstAPI = () =>
  http.get("Type/firstAPI?$format=JSON");

// 第二支 API
export const getSecondAPI = () =>
  http.get("Common/secondAPI?$format=JSON");

// 第三支 API
export const getThirdAPI = () =>
  http.get("IndustrialPark/thirdAPI?$format=JSON");

// 第四支 API
export const getFourthAPI = () =>
  http.get("Common/fourthAPI?$format=JSON");

實際在使用時,只要引入我們定義好的 API 函式即可。

import { getFirstAPI } from "@/api/request";

const fetchData = async () => {
  const data = await getFirstAPI();
  console.log(data);
};

之前踩過的坑

以前我會這樣寫,但是發現在一個檔案中,API 會觸發 2 次。

// request.js
export const firstApi = axios
  .get(url, config)
  .catch(handleError);

這樣的寫法等於:

// request.js
const firstApi = axios.get(...);//  立刻執行
export {firstApi };

所以在 import 之前就會打 API,而在程式中實際打 API 取資料時又會打一次 API,反而增加了無謂的請求次數:

// request.js
axios.get(...)  ← HTTP request 已經送出

回傳 Promise

Promise  export

總結

axios.create() 就像是幫你打造一台專屬的請求機器。你設定好它的出發點(baseURL)、運作規則(timeout)以及檢查標準(interceptors),之後在程式碼各處呼叫它時,就不必再重複撰寫這些瑣碎的設定。

您是正在為專案進行 API 封裝嗎?如果是的話,我可以幫您示範如何根據特定的 API 文件撰寫對應的請求函式。