搭建VUE項目時,需要考慮封裝一個全局的接口請求文件,可以對登錄進行攔截以及請求的攔截,還有對通用的狀態(tài)錯誤碼和異常進行處理,這樣外部使用接口請求時代碼會很簡潔。
以下是在網(wǎng)上找到的一個封裝接口文件的參考demo,是對axios庫的二次封裝:
import axios from "axios";
import { message } from "ant-design-vue";
import router from "../router";
/**
* 提示函數(shù)
* 禁止點擊蒙層、顯示一秒后關(guān)閉
*/
const tip = (msg) => {
message({
message: msg,
duration: 1000,
});
};
/**
* 跳轉(zhuǎn)登錄頁
* 攜帶當(dāng)前頁面路由,以期在登錄頁面完成登錄后返回當(dāng)前頁面
*/
const toLogin = () => {
router.replace({
path: "/login",
});
};
// 創(chuàng)建axios實例
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000, // request timeout
});
// 設(shè)置post請求頭
service.defaults.headers.post["Content-Type"] =
"application/x-www-form-urlencoded";
// 請求攔截器
service.interceptors.request.use(
(config) => {
// 登錄流程控制中,根據(jù)本地是否存在token判斷用戶的登錄情況
// 但是即使token存在,也有可能token是過期的,所以在每次的請求頭中攜帶token
// 后臺根據(jù)攜帶的token判斷用戶的登錄情況,并返回給我們對應(yīng)的狀態(tài)碼
// 而后我們可以在響應(yīng)攔截器中,根據(jù)狀態(tài)碼進行一些統(tǒng)一的操作。
const token = localStorage.getItem("token");
token && (config.headers.Authorization = token);
return config;
},
(error) => {
// 處理請求錯誤
return Promise.reject(error);
}
);
// 響應(yīng)攔截器
service.interceptors.response.use(
//請求成功
(response) => {
const res = response.data;
if (res.code !== 200 && res.status !== 200) {
return Promise.reject(res);
} else {
return res;
}
},
//請求失敗
(error) => {
const { response } = error;
if (response) {
// 請求已發(fā)出,但是不在30分鐘的范圍
errorHandle(response.status, response.data.message);
return Promise.reject(response);
} else {
// 處理斷網(wǎng)的情況
// eg:請求超時或斷網(wǎng)時,更新state的network狀態(tài)
// network狀態(tài)在app.vue中控制著一個全局的斷網(wǎng)提示組件的顯示隱藏
// 關(guān)于斷網(wǎng)組件中的刷新重新獲取數(shù)據(jù),會在斷網(wǎng)組件中說明
// store.commit('changeNetwork', false);
}
}
);
/**
* 請求失敗后的錯誤統(tǒng)一處理
* @param {Number} status 請求失敗的狀態(tài)碼
*/
const errorHandle = (status) => {
// 狀態(tài)碼判斷
switch (status) {
// 401: 未登錄狀態(tài),跳轉(zhuǎn)登錄頁
case 401:
toLogin();
break;
// 403 token過期
// 清除token并跳轉(zhuǎn)登錄頁
case 403:
localStorage.removeItem("token");
message({
message: "登錄過期,請重新登錄",
callback: (action) => {
console.log("action", action);
toLogin();
},
});
break;
// 404請求不存在
case 404:
tip("請求的資源不存在");
break;
}
};
export default service;
以下是我們項目組實際使用的request文件,axios + antDesignVue組件庫。
首先,需要在utils目錄下新增一個request.js文件:
import Vue from "vue";
import store from "../store/demo";
import router from "../router";
import axios from "axios";
import { message } from "ant-design-vue";
const vm = new Vue({ store, router });
let baseURL = "";
const service = axios.create({
baseURL: baseURL,
timeout: 120000,
});
// 請求攔截
service.interceptors.request.use(
(config) => {
config.headers["Content-type"] = "application/json; charset=utf-8;";
vm.$store.dispatch("global/setLoading", true);
return config;
},
(error) => {
console.log("error", error);
return Promise.reject(error);
}
);
// 響應(yīng)攔截
service.interceptors.response.use(
(response) => {
const status = response.status;
const code = response.data.status || response.data.code;
const res = response.data;
vm.$store.dispatch("global/setLoading", false);
if (status === 200) {
// 處理登錄失敗
const toLoginCode = ["00211", "00212"];
if (toLoginCode.indexOf(code) !== -1) {
sessionStorage.removeItem("token");
router.push({ path: "/to-login" });
return res;
}
// 處理成功
const successCode = ["I0000", "I0001"];
if (successCode.indexOf(code) !== -1) {
return res;
}
// 處理失敗
const errCode = ["Err001", "Err002", "Err003"];
if (errCode.indexOf(code) !== -1) {
message.error(res.msg || "服務(wù)器異常,請稍后重試!");
}
return Promise.reject(res);
} else {
return Promise.reject(new Error(res.msg || "Error"));
}
},
(error) => {
vm.$store.dispatch("global/setLoading", false);
let status;
try {
status.error.response.status;
} catch (error) {
status = 500;
}
message.error(`服務(wù)器異常,請稍后重試。當(dāng)前狀態(tài)碼:${status}`);
return Promise.reject(error);
}
);
其次,需要在API目錄下,新建一個如homeApi.js的文件:
// 引入剛剛創(chuàng)建的接口請求文件
import fetch from "../utils/request.js";
// 全局請求的參數(shù),如userId, token等,沒有可忽略
import { public } from "@/api/public.config.js";
// 接口地址,判斷測試、開發(fā)、生產(chǎn)
import { baseUrl } from "@/config/baseUrl.config.js";
export const login = (data)=>{
fetch(url:`${baseUrl}/server/login`, method: 'post',data:{public: public(),private:data })
}
最后,頁面上的具體使用實例:
// 引入用到的接口:
import { login } from ’@/api/homeApi‘
// 簡單示例:
setLogin(){
login().then(res=>{
console.log(res)
})
}
//復(fù)雜示例:
setLogin(info){
const params = {
userName: info
}
login(params ).then(res=>{
console.log(res)
// res.status === 'I0000' 是后端定義連接成功的狀態(tài)碼
if(res.status === 'I0000'){
const { list, total} = res
this.list = list
}
})
}
項目中的二次封裝具體進行了哪些操作?
默認配置有哪些
區(qū)分不同的環(huán)境(開發(fā)、測試、生產(chǎn))
配置了不同的baseURL
timeout超時時間
默認的請求方法是get
請求攔截器(在發(fā)送請求之前做些什么,錯誤了做些什么)
添加接口請求時的全局loading事件,以及配置了header中的token
響應(yīng)攔截器(對響應(yīng)數(shù)據(jù)做點什么,錯誤了做些什么)
取消loading事件
對后端返回的錯誤代碼進行分類處理
登錄失敗 跳轉(zhuǎn)到重新登陸頁面