請求系統
框架對 Nuxt 內建 $fetch 的封裝,提供 AES 加密傳輸、Token 自動注入與續期、統一回應格式解包(status/data/error/errorCode)和錯誤攔截能力。
基本用法
框架透過全域自動導入暴露 request() 函式:
// 基礎請求(預設 POST)
const data = await request('/api/user/info')
// 帶 body 的請求(自動序列化)
const result = await request('/api/login', {
body: { username: 'admin', password: '123456' },
})
// 帶查詢參數
const list = await request('/api/products', {
params: { page: 1, pageSize: 20, keyword: '搜尋詞' },
})
公開 plain JSON 中繼資料例外
OpenAPI operation 明確標記 x-sw-protocol: plain-json 時,generate-api 會略過該操作,不產生 request() 的加密客戶端包裝;此類中繼資料 GET 由 server 原生 $fetch 按後端契約存取,明確傳固定租戶識別,禁止轉發訪客 cookie、Authorization 或簽名參數。x-sw-visibility: public 只描述存取資格,不能取代傳輸協定標記。未標記的既有業務介面繼續走加密協定;未知 x-sw-protocol 會讓生成失敗。
本節新增 API 對應目前本地待發布改動;尚未發布。消費方採用前須核驗實際框架版本。
回應格式約定
框架約定後端介面回傳統一格式(回應體解密後),request() 自動解包 data 欄位:
// 後端回傳(解密後)
{ "status": "y", "data": { "id": 1, "name": "張三" } }
// request() 回傳值直接是 data 部分
const user = await request('/api/user/info')
// user = { id: 1, name: "張三" }
當 status === 'n' 時,request() 拋出 Error(message 為後端 error 欄位),不會自動顯示 toast —— 由呼叫方 try/catch 並給使用者回饋。
明文失敗信封:舊後端是混合協定——多數回應加密,但部分業務失敗(如登入介面的「帳號或密碼錯誤」4002)以明文 JSON 回傳,且 Content-Type 與加密回應相同。request() 會在解密前識別這類明文失敗信封並同樣拋 ApiError,業務側不必區分。只放行 status: 'n' 的明文;明文 status: 'y' 或任意 JSON 視為協定錯誤。解密失敗(金鑰 / transport 錯配、後端回傳非協定報文)統一拋以 [swift-web] 响应报文无法解密 開頭的 Error,不再冒出 Invalid typed array length 這類底層例外。
HTTP 200 但 body 為空
極少數情況下後端會回傳 HTTP 200 + 空 body。此時 request() 回傳 undefined(型別簽章上仍是 Promise<T>),不拋錯 —— 這是舊協定的既定行為,舊前端同樣原樣放行。
問題在於消費方拿到 undefined 會渲染成空狀態,與「後端真的沒資料」不可區分,出問題時排查無線索。所以 framework 在開發期會為此打一條告警(每個端點只出一次,生產建置不含該程式碼):
[swift-web] POST /movie/home 返回 HTTP 200 但 body 为空 → request() 返回 undefined。
看到這條告警時:先確認該端點是否本就允許回傳空。若不該為空,問題在後端,不要在前端加兜底把它掩蓋掉。
自動 Token 注入
request() 自動攜帶身分憑證:請求體統一包裝為 { data, token, deviceId }(token 取自 useUserStore().token),gzip + AES-CBC 加密後作為 text/plain 發送 —— 無需手動傳 headers。
上面以 text/plain 發送的是預設的 base64 傳輸編碼;對接裸二進位加密協定的後端時改配 request.transport: 'binary'(收發均為裸位元組流),見 Module Options · request。
配置
環境相關項放 .env(建置期由 nuxi 載入、烤入產物):
# .env —— 每個環境不同的值
NUXT_PUBLIC_API_BASE=https://api.example.com # 後端 API 基礎路徑
apiPrefix / apiVersion / apiKey 是專案常數(換環境也不變),直接寫在 nuxt.config.ts 的 runtimeConfig.public 實值裡隨程式碼走;執行期同名 NUXT_PUBLIC_* 程序環境變數仍可臨時覆蓋(Nuxt 原生機制)。
錯誤處理
try {
const data = await request('/api/user/sensitive-info')
} catch (e) {
// request() 失敗拋 Error(message 為後端 error 欄位或網路錯誤訊息)
// token 失效(errorCode 2002)已在內部靜默重登重試,業務無需單獨處理
showToast(e instanceof Error ? e.message : '請求失敗,請重試')
}
useFetch 與 request() 的選擇
| 場景 | 推薦方式 |
|---|---|
| 頁面初始資料(SSR) | useFetch() 或 useAsyncData() |
| 使用者操作觸發的請求 | request()(無響應式包裝,更簡潔) |
| 需要取消 / 重新請求 | useFetch()(自帶 abort 和 refresh) |
| 伺服器端 API route | $fetch()(Nitro 內建,無需 Token) |
Token 過期自動續期
framework 的 request() 在回應解包階段攔截 errorCode === 2002(token 失效),自動呼叫 silentLogin() 重新建立身份後帶 _retry=true 重發原請求一次,業務側零感知。再失敗拋 登入失效,請重新整理頁面 供上層兜底。
注意:該機制僅在 client 端生效。SSR 期 server 端遇到 2002 會按未登入處理(不重試),原因是 server 端無法重新建立設備身份(重新登入會寫 Set-Cookie 到回應頭,被 ISR 快取廣播)。
延伸閱讀
- 自動登入 + Token 重登流程 — 完整時序圖(含 2002 攔截重試鏈路)+ 三種渲染模式行為對照
- useAuthReady —
request()內部的 client 端門控機制 - 算繪模式快速指南 — 選 ISR / SSR / CSR 的反模式速查