一. 封裝各模塊作用
- 引入
一般我會在項(xiàng)目的src目錄中,新建一個(gè)request文件夾,然后在里面新建一個(gè)http.js和一個(gè)api.js文件。http.js文件用來封裝我們的axios,api.js用來統(tǒng)一管理我們的接口纲岭。
// 在http.js中引入axios
import axios from 'axios'; // 引入axios
import QS from 'qs'; // 引入qs模塊,用來序列化post類型的數(shù)據(jù)线罕,后面會提到
// vant的toast提示框組件止潮,大家可根據(jù)自己的ui組件更改。
import { Toast } from 'vant';
- 環(huán)境切換
我們的項(xiàng)目環(huán)境可能有開發(fā)環(huán)境闻坚、測試環(huán)境和生產(chǎn)環(huán)境沽翔。我們通過node的環(huán)境變量來匹配我們的默認(rèn)的接口url前綴兢孝。axios.defaults.baseURL可以設(shè)置axios的默認(rèn)請求地址就不多說了
if (process.env.NODE_ENV == 'development') {
axios.defaults.baseURL = '/api';
} else if (process.env.NODE_ENV == 'debug') {
axios.defaults.baseURL = '';
} else if (process.env.NODE_ENV == 'production') {
axios.defaults.baseURL = 'http://api.123dailu.com/';
}
- 請求頭的默認(rèn)配置
通過axios.defaults.timeout設(shè)置默認(rèn)的請求超時(shí)時(shí)間窿凤。例如超過了10s,就會告知用戶當(dāng)前請求超時(shí)跨蟹,請刷新等雳殊。 通過設(shè)置 withCredentials: true ,發(fā)送Ajax時(shí)窗轩,Request header中便會帶上 Cookie 信息琳猫。 post請求的時(shí)候雁比,我們需要加上一個(gè)請求頭吠勘,所以可以在這里進(jìn)行一個(gè)默認(rèn)的設(shè)置柴灯,即設(shè)置post的請求頭為application/x-www-form-urlencoded;charset=UTF-8
// 請求超時(shí)時(shí)間
axios.defaults.timeout = 10000;
// 在客戶端axios請求中設(shè)置
axios.defaults.withCredentials = true
// 通過設(shè)置 withCredentials: true ,發(fā)送Ajax時(shí)鞠呈,Request header中便會帶上 Cookie 信息。
// post請求頭
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
- 請求攔截器
我們在發(fā)送請求前可以進(jìn)行一個(gè)請求的攔截,為什么要攔截呢哺呜,我們攔截請求是用來做什么的呢?比如箕戳,有些請求是需要用戶登錄之后才能訪問的某残,或者post請求的時(shí)候,我們需要序列化我們提交的數(shù)據(jù)陵吸。這時(shí)候玻墅,我們可以在請求被發(fā)送之前進(jìn)行一個(gè)攔截,從而進(jìn)行我們想要的操作壮虫。
// 請求攔截器
axios.interceptors.request.use(
config => {
// 每次發(fā)送請求之前判斷是否存在token澳厢,如果存在,則統(tǒng)一在http請求的header都加上token囚似,不用每次請求都手動添加了
// 即使本地存在token赏酥,也有可能token是過期的,所以在響應(yīng)攔截器中要對返回狀態(tài)進(jìn)行判斷
const token = store.state.token;
token && (config.headers.Authorization = token);
return config;
},
error => {
return Promise.error(error);
}
)
注意:
這里說一下token谆构,一般是在登錄完成之后裸扶,將用戶的token通過localStorage或者cookie存在本地,然后用戶每次在進(jìn)入頁面的時(shí)候(即在main.js中)搬素,會首先從本地存儲中讀取token呵晨,如果token存在說明用戶已經(jīng)登陸過,則更新vuex中的token狀態(tài)熬尺。然后摸屠,在每次請求接口的時(shí)候,都會在請求的header中攜帶token粱哼,后臺人員就可以根據(jù)你攜帶的token來判斷你的登錄是否過期季二,如果沒有攜帶,則說明沒有登錄過揭措。這時(shí)候或許有些小伙伴會有疑問了胯舷,就是每個(gè)請求都攜帶token,那么要是一個(gè)頁面不需要用戶登錄就可以訪問的怎么辦呢绊含?其實(shí)桑嘶,你前端的請求可以攜帶token,但是后臺可以選擇不接收肮洹逃顶!
- 響應(yīng)攔截器
響應(yīng)攔截器很好理解讨便,就是服務(wù)器返回給我們的數(shù)據(jù),我們在拿到之前可以對他進(jìn)行一些處理以政。例如上面的思想:如果后臺返回的狀態(tài)碼是200霸褒,則正常返回?cái)?shù)據(jù),否則的根據(jù)錯(cuò)誤的狀態(tài)碼類型進(jìn)行一些我們需要的錯(cuò)誤盈蛮,其實(shí)這里主要就是進(jìn)行了錯(cuò)誤的統(tǒng)一處理和沒登錄或登錄過期后調(diào)整登錄頁的一個(gè)操作傲霸。
// 響應(yīng)攔截器
axios.interceptors.response.use(
response => {
if (response.status === 200) {
return Promise.resolve(response);
} else {
return Promise.reject(response);
}
},
// 服務(wù)器狀態(tài)碼不是200的情況
error => {
if (error.response.status) {
switch (error.response.status) {
// 401: 未登錄
// 未登錄則跳轉(zhuǎn)登錄頁面,并攜帶當(dāng)前頁面的路徑
// 在登錄成功后返回當(dāng)前頁面眉反,這一步需要在登錄頁操作昙啄。
case 401:
router.replace({
path: '/login',
query: { redirect: router.currentRoute.fullPath }
});
break;
// 403 token過期
// 登錄過期對用戶進(jìn)行提示
// 清除本地token和清空vuex中token對象
// 跳轉(zhuǎn)登錄頁面
case 403:
Toast({
message: '登錄過期,請重新登錄',
duration: 1000,
forbidClick: true
});
// 清除token
localStorage.removeItem('token');
store.commit('loginSuccess', null);
// 跳轉(zhuǎn)登錄頁面寸五,并將要瀏覽的頁面fullPath傳過去梳凛,登錄成功后跳轉(zhuǎn)需要訪問的頁面
setTimeout(() => {
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
});
}, 1000);
break;
// 404請求不存在
case 404:
Toast({
message: '網(wǎng)絡(luò)請求不存在',
duration: 1500,
forbidClick: true
});
break;
// 其他錯(cuò)誤,直接拋出錯(cuò)誤提示
default:
Toast({
message: error.response.data.message,
duration: 1500,
forbidClick: true
});
}
return Promise.reject(error.response);
}
}
);
二. 封裝后代碼
/**axios封裝
* 請求攔截梳杏、相應(yīng)攔截韧拒、錯(cuò)誤統(tǒng)一處理
*/
import axios from 'axios';
import QS from 'qs';
import { Toast } from 'vant';
import store from '../store/index'
// 環(huán)境的切換
if (process.env.NODE_ENV == 'development') {
axios.defaults.baseURL = '/api';
} else if (process.env.NODE_ENV == 'debug') {
axios.defaults.baseURL = '';
} else if (process.env.NODE_ENV == 'production') {
axios.defaults.baseURL = 'http://api.123dailu.com/';
}
// 請求超時(shí)時(shí)間
axios.defaults.timeout = 10000;
// 在客戶端axios請求中設(shè)置
axios.defaults.withCredentials = true
// 通過設(shè)置 withCredentials: true ,發(fā)送Ajax時(shí)十性,Request header中便會帶上 Cookie 信息叛溢。
// post請求頭
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
// 請求攔截器
axios.interceptors.request.use(
config => {
// 每次發(fā)送請求之前判斷是否存在token,如果存在劲适,則統(tǒng)一在http請求的header都加上token楷掉,不用每次請求都手動添加了
// 即使本地存在token,也有可能token是過期的霞势,所以在響應(yīng)攔截器中要對返回狀態(tài)進(jìn)行判斷
const token = store.state.token;
token && (config.headers.Authorization = token);
return config;
},
error => {
return Promise.error(error);
}
)
// 響應(yīng)攔截器
axios.interceptors.response.use(
response => {
if (response.status === 200) {
return Promise.resolve(response);
} else {
return Promise.reject(response);
}
},
// 服務(wù)器狀態(tài)碼不是200的情況
error => {
if (error.response.status) {
switch (error.response.status) {
// 401: 未登錄
// 未登錄則跳轉(zhuǎn)登錄頁面烹植,并攜帶當(dāng)前頁面的路徑
// 在登錄成功后返回當(dāng)前頁面,這一步需要在登錄頁操作愕贡。
case 401:
router.replace({
path: '/login',
query: { redirect: router.currentRoute.fullPath }
});
break;
// 403 token過期
// 登錄過期對用戶進(jìn)行提示
// 清除本地token和清空vuex中token對象
// 跳轉(zhuǎn)登錄頁面
case 403:
Toast({
message: '登錄過期草雕,請重新登錄',
duration: 1000,
forbidClick: true
});
// 清除token
localStorage.removeItem('token');
store.commit('loginSuccess', null);
// 跳轉(zhuǎn)登錄頁面,并將要瀏覽的頁面fullPath傳過去固以,登錄成功后跳轉(zhuǎn)需要訪問的頁面
setTimeout(() => {
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
});
}, 1000);
break;
// 404請求不存在
case 404:
Toast({
message: '網(wǎng)絡(luò)請求不存在',
duration: 1500,
forbidClick: true
});
break;
// 其他錯(cuò)誤墩虹,直接拋出錯(cuò)誤提示
default:
Toast({
message: error.response.data.message,
duration: 1500,
forbidClick: true
});
}
return Promise.reject(error.response);
}
}
);
/**
* get方法,對應(yīng)get請求
* @param {String} url [請求的url地址]
* @param {Object} params [請求時(shí)攜帶的參數(shù)]
*/
export function get(url, params){
return new Promise((resolve, reject) =>{
axios.get(url, {
params: params
})
.then(res => {
resolve(res.data);
})
.catch(err => {
reject(err.data)
})
});
}
/**
* post方法憨琳,對應(yīng)post請求
* @param {String} url [請求的url地址]
* @param {Object} params [請求時(shí)攜帶的參數(shù)]
*/
export function post(url, params) {
return new Promise((resolve, reject) => {
axios.post(url, QS.stringify(params))
.then(res => {
resolve(res.data);
})
.catch(err => {
reject(err.data)
})
});
}
三. http.js和 api.js
http.js是封裝好的axios, api.js 是把業(yè)務(wù)中接口 統(tǒng)一放在這個(gè)文件里,
具體根據(jù)項(xiàng)目進(jìn)行定制化的封裝可以參考axios封裝