目錄
- 1. 基本知識
- 2. Demo
- 3. 拓展
1. 基本知識
從實戰代碼中學習,上述實戰代碼來源:芋道源碼/yudao-mall-uniapp
該代碼中,通過自定義 request 函數對 HTTP 請求進行了統一管理,并且結合了 Token 認證機制
- 請求封裝原理,request 函數是對 uni.request 的一個封裝:
- 動態設置請求頭:根據 config 的配置,決定是否需要在請求頭中附加 Authorization(Bearer Token)。Token 是從本地存儲中獲取的
- 根據環境區分不同的 Base URL:根據當前的開發環境(development 或 production),動態設置請求的基礎 URL(baseUrl)
- 統一處理請求參數:config.params 會被轉化成查詢字符串,拼接到請求的 URL 后面
- Promise 封裝異步操作:請求通過 uni.request 發出,并將返回的 response 數據封裝為一個 Promise,使得調用 request 的地方可以使用 then 或 catch 來處理結果
- Token 認證管理原理
- Token 存儲:uni.setStorageSync 和 uni.getStorageSync 被用來在客戶端本地存儲 ACCESS_TOKEN 和 REFRESH_TOKEN,這兩個 Token 被用于身份驗證
- 獲取 Token:在每次 HTTP 請求時,首先會檢查請求是否需要 Token(通過 config.headers.isToken 判斷)。如果需要,就從本地存儲中獲取 AccessToken 并加入到請求頭中
- Token 過期處理:當請求返回的狀態碼為 401 時,表示 Token 已過期,此時會彈出提示框,讓用戶重新登錄并清除舊的 Token
- 錯誤處理機制
- 網絡錯誤:封裝了常見的網絡錯誤(如超時、服務器錯誤等),并提供了友好的提示
- 接口返回錯誤:統一處理接口返回的錯誤,錯誤信息根據 res.data.code 的值來決定,如果返回的是 500 錯誤或其他非 200 的錯誤,則通過 toast 提示給用戶
- 401 錯誤處理:當返回狀態碼為 401 時,表示 Token 過期或無效,代碼會自動處理登出流程
2. Demo
根據實戰中的Demo,給出一版通用的Demo:
封裝request的時候,需要與token結合:
// utils/request.js
import { getAccessToken, setToken, removeToken } from '@/utils/auth';
import config from '@/config';
import errorCode from '@/utils/errorCode';
import { toast, showConfirm } from '@/utils/common';let timeout = 10000;
let baseUrl = process.env.NODE_ENV === 'development' ? config.devbaseUrl : config.prodbaseUrl;const request = config => {const isToken = (config.headers || {}).isToken === false;config.header = config.header || {};if (getAccessToken() && !isToken) {config.header['Authorization'] = 'Bearer ' + getAccessToken();}config.header['tenant-id'] = '1'; // 強制設置租戶 IDif (config.params) {let url = config.url + '?' + tansParams(config.params);url = url.slice(0, -1);config.url = url;}return new Promise((resolve, reject) => {uni.request({method: config.method || 'get',timeout: config.timeout || timeout,url: config.baseUrl || baseUrl + config.url,data: config.data,header: config.header,dataType: 'json'}).then(response => {let [error, res] = response;if (error) {toast('后端接口連接異常');reject('后端接口連接異常');return;}const code = res.data.code || 200;const msg = errorCode[code] || res.data.msg || errorCode['default'];if (code === 401) {showConfirm('登錄狀態已過期,您可以繼續留在該頁面,或者重新登錄?').then(res => {if (res.confirm) {removeToken();uni.reLaunch({ url: '/pages/login' });}});reject('無效的會話,或者會話已過期,請重新登錄。');} else if (code === 500) {toast(msg);reject('500');} else if (code !== 200) {toast(msg);reject(code);}resolve(res.data);}).catch(error => {let { message } = error;if (message === 'Network Error') message = '后端接口連接異常';else if (message.includes('timeout')) message = '系統接口請求超時';else if (message.includes('Request failed with status code')) message = '系統接口' + message.substr(message.length - 3) + '異常';toast(message);reject(error);});});
};export default request;
對應的token文件:
// utils/auth.js
const AccessTokenKey = 'ACCESS_TOKEN';
const RefreshTokenKey = 'REFRESH_TOKEN';export function getAccessToken() {return uni.getStorageSync(AccessTokenKey);
}export function getRefreshToken() {return uni.getStorageSync(RefreshTokenKey);
}export function setToken(token) {uni.setStorageSync(AccessTokenKey, token.accessToken);uni.setStorageSync(RefreshTokenKey, token.refreshToken);
}export function removeToken() {uni.removeStorageSync(AccessTokenKey);uni.removeStorageSync(RefreshTokenKey);
}
相關接口請求:
// 在頁面中調用封裝的請求方法
import request from '@/utils/request';export default {methods: {fetchData() {request({url: '/api/getData',method: 'GET',params: { id: 123 }}).then(response => {console.log('數據:', response);}).catch(error => {console.log('請求失敗:', error);});}}
}
3. 拓展
process.env.NODE_ENV
是 Node.js 環境中用于獲取當前應用運行環境的一個變量
在大多數前端框架(如 Vue、React)以及后端框架(如 Express)中,process.env.NODE_ENV 被廣泛用于區分不同的開發環境
前端vue中可能已經標明了
在開發模式下:NODE_ENV=development npm run dev
在生產模式下:NODE_ENV=production npm run build
在 npm 腳本中,可以通過 cross-env 等工具來跨平臺設置環境變量:
"scripts": {"dev": "cross-env NODE_ENV=development vue-cli-service serve","build": "cross-env NODE_ENV=production vue-cli-service build"
}
另外一個接口超時時間,全局默認是20秒,如果時長不對,可以在單獨某個接口設置:
// 上傳圖片
uploadImage(data) {return upload({url: '/infra/file/upload',method: 'upload',filePath: data.filePath,timeout: 30000 // 設置超時時間為30秒});
}