有時候要根據項目的具體需求從新封裝http請求。 最近在公司作一個商城項目,由於我也是vue小白,在網上參考了不少資料,才把需求搞定。 以個人項目爲例,需求:vue
access_token
和 refresh_token
保存在localStorage),每次請求都要帶自定義請求頭 Authorization。access_token
過時後,用 refresh_token
從新請求刷新token,若是refresh_token
過時跳轉到登陸頁面從新獲取token。status
都是200(OK),請求成功 IsSuccess
爲 true
,請求失敗 IsSuccess
爲 false
。請求失敗會返回響應的錯誤碼 ErrorTypeCode
,10003 —— access_token
不存在或過時,10004 —— refresh_token
不存在或過時。有兩種請求,一種須要Token,一種不須要Token。這裏主要講第一種。ios
設置 request 和 response 攔截器。爲了減輕服務器的壓力,發起請求的時候先獲取服務器狀態,儲存在 localStorage,10分鐘內若是再有請求,再也不獲取狀態。 在request 攔截器中檢測服務器是否運行,是否有 access_token
,沒有就跳轉到登陸頁面。 最重要的是,實現 access_token
過時時,刷新token重發請求,這個須要在 response 攔截器中設置。element-ui
服務器生成 token 的過程當中,會有兩個時間,一個是 token 失效時間(access_token
過時時間),一個是 token 刷新時間(refresh_token
過時時間)。refresh_token
過時時間確定比 access_token
過時時間要長,當 access_token
過時時,能夠用 refresh_token
刷新 token。json
import axios from 'axios';
function getUrl(url) {
if (url.indexOf(baseUrl) === 0) {
return url;
}
url = url.replace(/^\//, '');
url = baseUrl + '/' + url;
return url;
}
function checkMaintenance() {
let status = {};
let url = getUrl('/GetMaintenanceState');
return axios({
url,
method: 'get'
})
.then(res => {
if (res.data.IsSuccess) {
status = {
IsRun: res.data.Value.IsRun, // 服務器是否運行
errMsg: res.data.Value.MaintenanceMsg // 維護時的信息
};
// localStorageSet 爲封裝好的方法,儲存字段的同時,儲存時間戳
localStorageSet('maintenance', status);
// 傳遞獲取的結果
return Promise.resolve(status);
}
})
.catch(() => {
return Promise.reject();
});
}
複製代碼
function getRefreshToken() {
let url = getUrl('/Token');
// 登陸時已經獲取token儲存在localStorage中
let token = JSON.parse(localStorage.getItem('token'));
return axios({
url,
method: 'post',
data: 'grant_type=refresh_token&refresh_token=' + token.refresh_token,
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
// 開發者密鑰
Authorization: 'Basic xxxxxxxxxxx'
}
})
.then(res => {
if (res.data.IsSuccess) {
var token_temp = {
access_token: res.data.access_token,
refresh_token: res.data.refresh_token
};
localStorage.setItem('token', JSON.stringify(token_temp));
// 將access_token儲存在session中
sessionStorage.setItem('access_token', res.data.access_token);
return Promise.resolve();
}
})
.catch(() => {
return Promise.reject();
});
}
複製代碼
由於要封裝不一樣需求的請求,最好建立 axios 實例(這裏主要是最複雜的請求)axios
request 攔截器:api
import router from '../router';
import { Message } from 'element-ui';
const instance = axios.create();
instance.interceptors.request.use(
config => {
// 獲取儲存中本地的維護狀態,localStorageGet方法,超過10分鐘返回false
let maintenance = localStorageGet('maintenance');
// 若是本地不存在 maintenance 或 獲取超過10分鐘,從新獲取
if (!maintenance) {
return checkMaintenance()
.then(res => {
if (res.IsRun) {
// 獲取session中的access_token
let access_token = sessionStorage.getItem('access_token');
// 若是不存在字段,則跳轉到登陸頁面
if (!access_token) {
router.push({
path: '/login',
query: { redirect: router.currentRoute.fullPath }
});
// 終止這個請求
return Promise.reject();
} else {
config.headers.Authorization = `bearer ${access_token}`;
}
config.headers['Content-Type'] = 'application/json;charset=UTF-8';
// 這一步就是容許發送請求
return config;
} else {
// 若是服務器正在維護,跳轉到維護頁面,顯示維護信息
router.push({
path: '/maintenance',
query: { redirect: res.errMsg }
});
return Promise.reject();
}
})
.catch(() => {
// 獲取服務器運行狀態失敗
return Promise.reject();
});
} else { // 本地存在 maintenance
if (maintenance.IsRun) {
let access_token = sessionStorage.getItem('access_token');
if (!access_token) {
router.push({
path: '/login',
query: { redirect: router.currentRoute.fullPath }
});
return Promise.reject();
} else {
config.headers.Authorization = `bearer ${access_token}`;
}
config.headers['Content-Type'] = 'application/json;charset=UTF-8';
return config;
} else {
router.push({
path: '/maintenance',
query: { redirect: maintenance.errMsg }
});
return Promise.reject();
}
}
},
err => {
// err爲錯誤對象,可是在個人項目中,除非網絡問題纔會出現
return Promise.reject(err);
}
);
複製代碼
response 攔截器:服務器
這只是針對我這個項目的狀況,由於全部請求都是成功的,靠ErrorTypeCode錯誤碼區分,因此在response回調中處理。網絡
如果普通狀況,token 過時返回狀態碼 401,應該中err回調中處理。session
instance.interceptors.response.use(
response => {
// access_token不存在或過時
if (response.data.ErrorTypeCode === 10003) {
return getRefreshToken()
.then(() => {
// 從新設置
let access_token = sessionStorage.getItem('access_token');
config.headers.Authorization = `bearer ${access_token}`;
config.headers['Content-Type'] = 'application/json;charset=UTF-8';
// 從新請求
// 若是請求的時候refresh_token也過時
return instance(config).then(res => {
if (res.data.ErrorTypeCode === 10004) {
router.push({
path: '/login',
query: { redirect: router.currentRoute.fullPath }
});
return Promise.reject();
}
// 使響應結果省略data字段
return Promise.resolve(response.data);
});
})
.catch(() => {
// refreshtoken 獲取失敗就只能到登陸頁面
router.push({
path: '/login',
query: { redirect: router.currentRoute.fullPath }
});
return Promise.reject();
});
}
// refresh_token不存在或過時
if (response.data.ErrorTypeCode == 10004) {
router.push({
path: '/login',
query: { redirect: router.currentRoute.fullPath }
});
return Promise.reject();
}
// 使響應結果省略data字段
return response.data;
},
err => {
return Promise.reject(err);
}
);
複製代碼
function request({ url, method, Value = null }) {
url = getUrl(url);
method = method.toLowerCase() || 'get';
let obj = {
method,
url
};
if (Value !== null) {
if (method === 'get') {
obj.params = { Value };
} else {
obj.data = { Value };
}
}
return instance(obj)
.then(res => {
return Promise.resolve(res);
})
.catch(() => {
Message.error('請求失敗,請檢查網絡鏈接');
return Promise.reject();
});
}
// 向外暴露成員
export function get(setting) {
setting.method = 'GET';
return request(setting);
}
export function post(setting) {
setting.method = 'POST';
return request(setting);
}
複製代碼
import { post, get } from '@/common/network';
post({
url: '/api/xxxxx',
Value: {
GoodsName,
GoodsTypeId
}
}).then(res => {
//.....
})
複製代碼
以上封裝只是針對這個項目的需求,但願能對你有所幫助app