Vue功能篇 - 3.封装axios请求库,支持取消请求和刷新token

每个项目的需求各有偏差,务必根据自己的项目调整相关配置
如大家有什么好的建议,可在评论区回复!

  • 动态地址(需注释baseUrl)
    - 在public创建config.js,里面为api地址
    - 各个api文件夹中引入即可,然后拼接到api上
  • 静态地址
    配置baseUrl即可

1.创建 httpload.js 文件

import qs from 'qs';
import axios from 'axios';
import { message } from 'element-plus';
import router from '../router';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
message.config({
  duration: 2,
  maxCount: 3
});
NProgress.configure({ showSpinner: false });

const http = axios.create({
    baseURL: process.env.API_HOST, //设置请求的base url,也可不设置这里,在api文件里直接拼接api
    timeout: 300000, //超时时长5分钟,
    crossDomain: true
});

http.defaults.headers.post['Content-Type'] ='multipart/form-data;application/json;charset=UTF-8;';

//当前正在请求的数量
let needLoadingRequestCount = 0;

// 显示loading
function showLoading() {
  // 后面这个判断很重要,因为关闭时加了抖动,此时loading对象可能还存在,
  // 但needLoadingRequestCount已经变成0.避免这种情况下会重新创建个loading
  if (needLoadingRequestCount === 0) {
    NProgress.start();
  }
  needLoadingRequestCount++;
}

// 隐藏loading
function hideLoading() {
  needLoadingRequestCount--;
  needLoadingRequestCount = Math.max(needLoadingRequestCount, 0); // 做个保护
  if (needLoadingRequestCount === 0) {
    NProgress.done();
  }
}


//添加请求拦截器
let tempConfig = '';
http.interceptors.request.use(config => {
    //判断当前请求是否设置了不显示Loading
    if (config.headers.showLoading !== false) {
        showLoading();
    }
    config.url = decodeURI(encodeURI(config.url).replace('%E2%80%8B', "")) //去除url中的空格
    const token = sessionStorage.token;
    if (token) {
        config.headers.common["Authorization"] = 'Bearer' + token;
    }
    tempConfig = config;
    return config;
}, err => {
    //判断当前请求是否设置了不显示Loading
    if (config.headers.showLoading !== false) {
        hideLoading();
    }
    message.error('请求超时!');
    return Promise.resolve(err);
});

//响应拦截器
http.interceptors.response.use(
    response => {
        //判断当前请求是否设置了不显示Loading(不显示自然无需隐藏)
        if (response.config.headers.showLoading !== false) {
            hideLoading();
        }
     //如有刷新token的需求,可放开以下代码
    //if (response.status === 401) {
      //return refreshTokenFuc(tempConfig);
   // }
	return response;
    },
    error => {
        hideLoading();
		if(error.response&&error.response.status){
			switch (error.response.status) {
				//401: 未登录-授权过期
				case 401:
				//如有刷新token的需求,可放开以下代码
				//return refreshTokenFuc(error.config);
				break;
				case 500:
				 message.error(error.response.data.message);
				break;
				case 503:
				 message.error('温馨提示:服务不可用,状态码:503');
				break;
			}
			return Promise.reject(error.response);
		}
		  if (axios.isCancel(error)) {
      		//取消请求
    	  } else {
     	  message.error('服务器打瞌睡了');
    		}
        return Promise.reject(error.response);
    }
);

export default http;


2.创建api文件夹,将以下代码放入index.js

  • api封装后导出供页面使用
  • 可以直接拼接url,此时可不配置httpload里的baseurl
import request from "@/http/httpload.js"
	export default{
		getData(data){
		return request({
			url:"/api/xxx",
			method:"post",
			data:data
		})
    },
    getManyData(data){
		return request({
			url:"/api/xxx",
			method:"get",
			parmas:data
		})
	}
}

3.vue页面中调用

<template>
  <div>
    <el-button class="el-icon-user" @click="getData">获取数据</el-button>
  </div>
</template>
<script>
import Task from "@/api/index.js";
export default {
  name: "testDemo",
  methods: {
    getData() {
      var data={id:'xxxxxxx'}
      Task.getData(data).then(res => {
         console.log(res);
      }).catch(error=>{
        console.log(error);
      });
    }
  }
};
</script>

4.刷新refreshToken 代码 [非必须,根据具体需求使用]

如有刷新token的需求,将以下代码放入httpload.js文件中即可,
注:以下代码必须放到 响应拦截请求前

待解决问题:刷新refreshToken后,新Token获取正常,并重新自动请求了之前错误的请求,数据返回是正常的,但页面并不会重新渲染,因为数据请求时已经catch了! 目前暂无好的解决方法,需要用户点击一下页面上任意的按钮或者刷新页面即可

//页面重定向
function pageRedirect() {
  message.error('登录信息已过期,请重新登录!');
  setTimeout(() => {
    //routeRedirect(); 跳转页面
  }, 1500);
}

//检测值是否存在
function checkRefreshIsExit(token) {
  if (!token || token === null || token === 'undefined') {
    return false;
  }
  return true;
}

let isRefreshToken = false; // 正在刷新token
let refreshCount = 0;
// 刷新token方法
async function refreshTokenFuc(config) {
  refreshCount++;
  if (!isRefreshToken) {
    const temp = {
      client_id: 'login',
      client_secret: '123456',
      grant_type: 'refresh_token',
      refresh_token: isUseBaseToken ? sessionStorage.refreshBaseToken : sessionStorage.refreshToken,
    };
    const isExitToken = await checkRefreshIsExit(temp.refresh_token);
    if (!isExitToken) {
      pageRedirect();
      return;
    }
    return TokenApi.userToken(temp).then((res) => {
      isRefreshToken = true;
      const { data } = res;
      if (data.code > -1) {
         let tempToken = data.access_token;
          sessionStorage.baseToken = data.access_token;
          sessionStorage.refreshBaseToken = data.refresh_token;
        
        config.headers.Authorization = `Bearer ${tempToken}`;
        return http(config);
      } else {
        pageRedirect();
      }

    }).catch((error) => {
      if (refreshCount === 3 || error.status === 400 || !sessionStorage.baseToken) {
        pageRedirect();
      } else {
        refreshTokenFuc(config);
      }
    }).finally(() => {
      refreshCount = 0;
      isRefreshToken = false;
    })
  }
}

5.取消请求及阻止重复请求 [非必须,根据具体需求使用]

通过AbortController将多次访问的api取消

let httpList = [];

const removeHttp = config => {
  const { method, url, params, data } = config;
  const requestKey = [method, url, qs.stringify(params), qs.stringify(data)].join('&');
  let index = httpList.findIndex(v => {
    const tempRequestKey = [v.method, v.url, qs.stringify(v.params), qs.stringify(v.data)].join('&');
    if (requestKey === tempRequestKey) {
      return true;
    }
  });
  if (index >= 0) {
    httpList[index].controller.abort();
    httpList.splice(index, 1);
  }
}


http.interceptors.request.use(
  (config) => {
  removeHttp(config);
  //....其它省略
  
  //增加自动取消重复请求功能
    const controller = new AbortController();
    config.signal = controller.signal;
    config.controller = controller;
    httpList.push({ ...config });
}


// 响应拦截器
http.interceptors.response.use(
  (response) => {
    //.....其它省略
  },
  (error) => {
  //.....其它省略
  if (axios.isCancel(error)) {
      //取消请求
    } else {
      message.error('温馨提示:数据获取失败');
    }
  //.....其它省略
},
  • 47
    点赞
  • 179
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
Vue中使用axios进行网络请求是非常常见的。我们可以对axios进行封装来方便地在Vue中使用。这里提供一个例子,包括axios的安装、请求拦截器、设置请求超时时间以及post请求头的设置。 首先,我们需要安装axios,可以通过npm install axios命令来进行安装。后,我们可以在Vue项目中的main.js文件中引入axios,并且将其挂载到Vue原型上,这样在组件中就可以通过this.$http来进行访问。接下来,我们可以封装一个request函数,封装axios请求拦截器、设置请求超时时间以及post请求头的设置。 下面是一个简单的封装axios请求的例子: 1. 安装axios ``` npm install axios ``` 2. 在main.js中引入axios并挂载到Vue原型上 ``` import axios from 'axios' Vue.prototype.$http = axios ``` 3. 封装request函数 ``` import axios from 'axios' import store from '@/store/index' // 创建axios实例 const service = axios.create({ timeout: 10000 // 请求超时时间 }) // 请求拦截器 service.interceptors.request.use( config => { // 每次发送请求之前判断vuex中是否存在token // 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况 // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断 const token = store.state.token token && (config.headers.Authorization = token) return config }, error => { return Promise.error(error) } ) // 响应拦截器 service.interceptors.response.use( response => { // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据 // 否则的话抛出错误 if (response.status === 200) { return Promise.resolve(response.data) } else { return Promise.reject(response) } }, error => { // 服务器返回不是 2 开头的情况,会进入这个回调 // 可以根据后台返回的状态码进行不同的操作 if (error.response.status) { switch (error.response.status) { // 401: 未登录 // 未登录则跳转登录页面,并携带当前页面的路径 // 在登录成功后返回当前页面,这一步需要在登录页操作。 case 401: router.replace({ path: '/login', query: { redirect: router.currentRoute.fullPath } }) break // 403 token过期 // 登录过期对用户进行提示 // 清除本地token和清空vuex中token对象 // 跳转登录页面 case 403: Toast({ message: '登录过期,请重新登录', duration: 1000, forbidClick: true }) // 清除token localStorage.removeItem('token') store.commit('loginSuccess', null) // 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面 setTimeout(() => { router.replace({ path: '/login', query: { redirect: router.currentRoute.fullPath } }) }, 1000) break // 404请求不存在 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) } } ) export default service ``` 4. 在组件中使用request函数 ``` import request from '@/utils/request' // 发送get请求 request.get(url, params).then(res => { console.log(res) }).catch(err => { console.log(err) }) // 发送post请求 request.post(url, data).then(res => { console.log(res) }).catch(err => { console.log(err) }) ``` --相关问题--:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

情系半生e

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值