iT邦幫忙

2025 iThome 鐵人賽

DAY 22
1
Vue.js

打造銷售系統30天修練 - 全集中・Vue之呼吸系列 第 22

Day 22:[APIの呼吸・壹之型] Backend連接 - Axios設定與封裝

  • 分享至 

  • xImage
  •  

在過去幾天的修煉中,我們已經建立起一套完整的登入與狀態管理機制。在上一回,我們甚至在 authStore 中定義了一個名為 fetchUserProfile 的 action,準備在應用程式初始化時去獲取使用者資料。但我們留下了一行code:

// const response = await apiClient.get('/me');

這行程式碼代表了我們前端應用與後端世界溝通的橋樑。今天,我們將修煉「APIの呼吸・壹之型:Backend連接」,學習如何使用 axios 將其封裝成一個易於管理、可重複使用的模組。

為何選擇 Axios?

雖然瀏覽器內建了 fetch API,但 axios 提供了更多在大型應用中不可或缺的功能:

  • 攔截器 (Interceptors):能在請求發送前或回應接收後進行攔截處理,是實現統一邏輯的關鍵。
  • 更豐富的配置:例如 baseURLtimeout 等。
  • 更佳的錯誤處理:能清晰地區分不同類型的錯誤。
  • 瀏覽器相容性:在舊版瀏覽器中有更好的支援。

因此,axios 長期以來都是 Vue 中進行 HTTP 通訊的首選。

Step 1: 安裝與建立實例

首先,將 axios 加入我們的專案。

npm install axios

接著,一個好的實踐是建立一個專門的檔案來管理 axios 的設定,而不是在每個元件中都 import axios。讓我們在 src 下建立一個新資料夾 api,並在其中建立 client.js

// src/api/client.js

import axios from 'axios';

// 使用 axios.create() 建立一個新的 axios 實例
const apiClient = axios.create({
  // 從環境變數讀取後端 API 的基礎 URL
  baseURL: import.meta.env.VITE_API_BASE_URL,
  // 設定請求超時時間,單位為毫秒
  timeout: 10000,
  // 設定通用的請求標頭
  headers: {
    'Content-Type': 'application/json',
  },
});

export default apiClient;

我們還需要在 .env 檔案中加入後端 API 的網址:

# .env
VITE_API_BASE_URL="https://your-backend-api.com/api"

Step 2: 攔截器 - API Client 的靈魂

攔截器是 axios 最強大的功能,它允許我們在請求或回應被處理前,先對其進行攔截和修改。

請求攔截器:自動附加 Token

我們不希望在每次需要驗證的 API 請求中,都手動加上 Authorization 標頭。請求攔截器可以完美地自動化這個過程。

修改 src/api/client.js

// src/api/client.js
import axios from 'axios';
import { useAuthStore } from '../stores/auth';

// ... apiClient 的建立

// 請求攔截器
apiClient.interceptors.request.use(
  (config) => {
    const authStore = useAuthStore();
    const token = authStore.token;

    if (token) {
      // 如果 token 存在,則在每個請求的標頭中加入 Authorization
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    // 對請求錯誤做些什麼
    return Promise.reject(error);
  },
);

export default apiClient;

現在,只要我們是登入狀態,所有透過 apiClient 發出的請求,都會自動帶上 Bearer Token

回應攔截器:全域錯誤處理

如果 token 過期或無效,後端通常會回傳 401 Unauthorized 狀態碼。我們不希望在每個 API 呼叫中都寫一次處理 401 的邏輯。回應攔截器可以幫我們集中處理。

繼續修改 src/api/client.js

// src/api/client.js
// ...
import router from '../router'; // 引入 router

// ...

// 回應攔截器
apiClient.interceptors.response.use(
  (response) => {
    // 狀態碼為 2xx 的情況
    // 直接回傳回應,或只回傳 response.data
    return response;
  },
  (error) => {
    // 狀態碼非 2xx 的情況
    if (error.response) {
      switch (error.response.status) {
        // 401: 未授權,通常是 token 無效或過期
        case 401:
          {
            const authStore = useAuthStore();
            // 清除本地的使用者狀態
            authStore.logout();
            // 強制跳轉到登入頁面
            router.push({ name: 'login' });
            // 可以在這裡顯示一個錯誤提示,例如「登入已過期,請重新登入」
            console.error('Unauthorized, logging out.');
          }
          break;

        // 其他錯誤狀態碼,例如 404, 500 等
        default:
          console.error(`發生錯誤: ${error.response.status}`);
          break;
      }
    }
    // 將錯誤繼續拋出,讓呼叫的地方可以單獨處理
    return Promise.reject(error);
  },
);

export default apiClient;

這個回應攔截器極大地簡化了我們的錯誤處理。任何地方的 API 呼叫,一旦遇到 401,都會被自動登出並導向登入頁,無需在元件中編寫任何額外程式碼!

Step 3: API 模組化與整合

最後,我們將 API 請求本身也封裝成模組,讓元件的職責更單純。

建立 src/api/auth.js

// src/api/auth.js
import apiClient from './client';

// 獲取當前登入使用者的資料
export const fetchUserProfile = () => {
  return apiClient.get('/me');
};

// (範例)驗證來自 Google 的 token
export const verifyGoogleToken = (googleToken) => {
  return apiClient.post('/auth/google-verify', { token: googleToken });
};

現在,我們可以回到 src/stores/auth.js,修改一下原本的程式碼。

// src/stores/auth.js
import { defineStore } from 'pinia';
import { fetchUserProfile } from '../api/auth'; // 引入封裝好的 API 函式

export const useAuthStore = defineStore('auth', {
  // ... state, getters
  actions: {
    // ... login, logout
    async fetchUserProfile() {
      if (this.token) {
        try {
          const response = await fetchUserProfile();
          this.user = response.data;
        } catch (error) {
          // 因為攔截器會處理 401,所以這裡主要處理其他可能的錯誤
          // 如果 token 無效,攔截器會自動登出,這個 catch 可能不會被執行
          console.error('獲取使用者資料失敗', error);
        }
      }
    },
  },
});

總結

今天,透過封裝 axios,我們實現了:

  1. 統一的 API 設定baseURLtimeout 等設定集中管理。
  2. 自動化的 Token 注入:透過「請求攔截器」,讓每個請求自動攜帶認證標頭。
  3. 全域的錯誤處理:透過「回應攔截器」,實現了 401 自動登出等強大功能。
  4. 模組化的 API 管理:讓元件的程式碼更乾淨,專注於 UI 邏輯。

這套 API Client 的架構,不僅讓我們當前的程式碼更健壯,也為未來新增更多複雜的 API 請求打下了良好的基礎。

明日,Day 23:[APIの呼吸・貳之型] 錯誤處理 - 攔截器與重試機制。心を燃やせ 🔥!


上一篇
Day 21:[Stateの呼吸・貳之型] User State - 管理登入狀態與資料
系列文
打造銷售系統30天修練 - 全集中・Vue之呼吸22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言