axios 基于 promise 的 http 库 特性:拦截请求和响应、取消请求、转换 json、客户端防御 XSRF
当后端接口报了 500 错误时被 axios 拦截了但确并未返回一个 promise,导致业务代码中未捕获此错误。
所以记住:
在每个 promise 链条中必须返回 promise,否则调用结果可能和你预期不一样。
1 2 3 4 5 6 7 8 9 10 11 12
| service.interceptors.response.use( (response) => { if (response.status === 200 && response.data) { return response.data; } else { return Promise.reject(new Error("请求失败")); } }, (error) => { return Promise.reject(error); } );
|
1、axios 的安装
2、在 request 文件夹 http.js
1 2 3 4
| import axios from "axios"; import QS from "qs"; import { Toast } from "vant";
|
3、环境的切换/请求超时/post 请求头设置
1 2 3 4 5 6 7 8 9 10 11 12 13
| if (process.env.NODE_ENV == "development") { axios.defaults.baseURL = "https://www.baidu.com"; } else if (process.env.NODE_ENV == "debug") { axios.defaults.baseURL = "https://www.ceshi.com"; } else if (process.env.NODE_ENV == "production") { axios.defaults.baseURL = "https://www.production.com"; }
axios.defaults.timeout = 10000;
axios.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded;charset=UTF-8";
|
4、请求拦截 响应拦截
点击展示代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
|
import store from '@/store/index';
axios.interceptors.request.use( config => { const token = store.state.token; token && (config.headers.Authorization = token); return config; }, error => { return Promise.error(error); } )
axios.interceptors.response.use( response => { if (response.status === 200) { return Promise.resolve(response); } else { return Promise.reject(response); } }, error => { if (error.response.status) { switch (error.response.status) { case 401: router.replace({ path: '/login', query: { redirect: router.currentRoute.fullPath } }); break; case 403: Toast({ message: '登录过期,请重新登录', duration: 1000, forbidClick: true }); localStorage.removeItem('token'); store.commit('loginSuccess', null); setTimeout(() => { router.replace({ path: '/login', query: { redirect: router.currentRoute.fullPath } }); }, 1000); break;
case 404: Toast({ message: '网络请求不存在', duration: 1500, forbidClick: true }); break; default: Toast({ message: error.response.data.message, duration: 1500, forbidClick: true }); } return Promise.reject(error.response); } } });
|
5、get、post 请求的封装
get 方法:我们通过定义一个 get 函数,get 函数有两个参数,第一个参数表示我们要请求的 url 地址,第二个参数是我们要携带的请求参数。get 函数返回一个 promise 对象,当 axios 其请求成功时 resolve 服务器返回 值,请求失败时 reject 错误值。最后通过 export 抛出 get 函数。
点击展示代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
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); }); }); }
|
原理同 get 基本一样,但是要注意的是,post 方法必须要使用对提交从参数对象进行序列化的操作,所以这里我们通过 node 的 qs 模块来序列化我们的参数。这个很重要,如果没有序列化操作,后台是拿不到你提交的数据的。这就是文章开头我们 import QS from ‘qs’;的原因
点击展示代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
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); }); }); }
|
6、axios 完整封装代码:
点击展示代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
|
import axios from "axios"; import router from "../router"; import store from "../store/index"; import { Toast } from "vant";
const tip = (msg) => { Toast({ message: msg, duration: 1000, forbidClick: true, }); };
const toLogin = () => { router.replace({ path: "/login", query: { redirect: router.currentRoute.fullPath, }, }); };
const errorHandle = (status, other) => { switch (status) { case 401: toLogin(); break; case 403: tip("登录过期,请重新登录"); localStorage.removeItem("token"); store.commit("loginSuccess", null); setTimeout(() => { toLogin(); }, 1000); break; case 404: tip("请求的资源不存在"); break; default: console.log(other); } };
var instance = axios.create({ timeout: 1000 * 12 });
instance.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";
instance.interceptors.request.use( (config) => { const token = store.state.token; token && (config.headers.Authorization = token); return config; }, (error) => Promise.error(error) );
instance.interceptors.response.use( (res) => (res.status === 200 ? Promise.resolve(res) : Promise.reject(res)), (error) => { const { response } = error; if (response) { errorHandle(response.status, response.data.message); return Promise.reject(response); } else { if (!window.navigator.onLine) { store.commit("changeNetwork", false); } else { return Promise.reject(error); } } } );
export default instance;
|
7、token 失效及接口国际化
点击展示代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
|
export function isCheckTimeout() { var currentTime = Date.now(); var timeStamp = getTimeStamp(); return currentTime - timeStamp > TOKEN_TIMEOUT_VALUE; }
import axios from "axios"; import store from "@/store"; import { ElMessage } from "element-plus"; import { isCheckTimeout } from "@/utils/auth";
const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 5000, });
service.interceptors.request.use( (config) => { if (store.getters.token) { if (isCheckTimeout()) { store.dispatch("user/logout"); return Promise.reject(new Error("token 失效")); } config.headers.Authorization = `Bearer ${store.getters.token}`; } config.headers["Accept-Language"] = store.getters.language; return config; }, (error) => { return Promise.reject(error); } );
service.interceptors.response.use( (response) => { const { success, message, data } = response.data; if (success) { return data; } else { ElMessage.error(message); return Promise.reject(new Error(message)); } }, (error) => { if ( error.response && error.response.data && error.response.data.code === 401 ) { store.dispatch("user/logout"); } ElMessage.error(error.message); return Promise.reject(error); } );
export default service;
|