上一篇文章中向大家介紹了 用 birc-axios 一鍵打造 Next.js 企業級網路請求架構 ,裡頭有說到,這個套件會在你的專案中新增以下檔案:
services/
├── api.ts
├── request.ts
├── response.ts
└── example/
├── exampleAPI.ts
└── exampleType.ts
這篇文章就是來講講這些檔案的設計想法並拆解說明~!
實現 Axios 的其中一種做法,也就是birc-axios的做法,是會需要兩個檔案:第一個是 API 核心設定檔(建立實例、攔截器),第二個是業務 API 封裝檔(定義具體的請求方法)。
api.ts)這個檔案建立了一個自定義的 Axios 實例(Instance),並設定了「請求攔截器」與「回應攔截器」。
import axios, { InternalAxiosRequestConfig } from 'axios';
// 1. 建立自定義的 Axios 實例
const API = axios.create({ 'baseURL': process.env.NEXT_PUBLIC_API_BASE_URL });
axios.create():建立一個帶有共用設定的 Axios 物件。這裡設定了 baseURL(API 的基礎網址,從 Next.js 的環境變數中讀取),以後你發送請求時只要寫 /users,它會自動拼成 [https://api.yourdomain.com/users](https://api.yourdomain.com/users)。// 2. 請求攔截器 (Request Interceptor)
API.interceptors.request.use(function (config: InternalAxiosRequestConfig) {
// 確保 headers 物件存在
if (!config.headers) {
config.headers = new axios.AxiosHeaders();
}
// 預設 Content-Type 為 json
if (!config.headers['Content-Type']) {
config.headers['Content-Type'] = 'application/json';
}
// 自動帶入 Token (JWT)
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
});
localStorage 檢查有沒有登入用的 token。如果有的話,會自動把它塞進請求的 Headers 裡的 Authorization。這樣你就不用在每一個 API 請求手動寫帶 Token 的邏輯了。// 3. 回應攔截器 (Response Interceptor)
API.interceptors.response.use(
(response) => { return response.data }, // 成功直接回傳資料本體
(error) => {
// 統一錯誤處理
if (error.response) {
return Promise.reject(error.response.data);
} else if (error.request) {
return Promise.reject("No response from server");
} else {
return Promise.reject(error.message);
}
}
);
export default API;
response => response.data):Axios 預設的回傳物件裡面包了很多東西(包含 status、headers 等)。這裡直接把資料本體 (response.data) 萃取出來,以後你在元件呼叫時,就不用每次都寫 res.data 才能拿到資料。error => ...):將各種網路或伺服器錯誤(如 400, 403, 500)做統一的分類與簡化,方便前端頁面 catch 到精簡的錯誤訊息。CouponAPI)這個檔案展示了如何使用上面做好的 API 來管理特定功能的請求(以「優惠券 Coupon」為例)。
import API from '@/services/api';
import { Response } from '@/services/response';
import { Coupon, Data } from '@/services/example/exampleType';
const BASE_URL = 'coupon';
const CouponAPI = {
// 取得單一優惠券 (透過 ID)
'get': (id: string): Promise<Response<Coupon>> =>
API.get(`/${BASE_URL}/${id}`),
'search': (): Promise<Response<Data[]>> =>
API.get(`/${BASE_URL}`),
};
export default CouponAPI;
CouponAPI 這個物件裡面。Promise<Response<Coupon>>),讓前端開發時能享有完整的程式碼自動補齊(IntelliSense)與型別檢查。const couponData = await CouponAPI.get('123');