项目中axios请求封装

// 环境的切换
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';
}

第一种

import axios from 'axios';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Session } from '/@/utils/storage';

// 配置新建一个 axios 实例
const request = axios.create({
	baseURL: import.meta.env.VITE_API_URL as any,
	timeout: 50000,
	headers: { 'Content-Type': 'application/json' },
});

// 添加请求拦截器
request.interceptors.request.use(
	(config) => {
		// 在发送请求之前做些什么 token
		if (Session.get('token')) {
			config.headers.common['Authorization'] = `${Session.get('token')}`;
		}
		return config;
	},
	(error) => {
		// 对请求错误做些什么
		return Promise.reject(error);
	}
);

// 添加响应拦截器
request.interceptors.response.use(
	(response) => {
		// 对响应数据做点什么
		const res = response.data;
		if (res.code && res.code !== 0) {
			// `token` 过期或者账号已在别处登录
			if (res.code === 401 || res.code === 4001) {
				Session.clear(); // 清除浏览器全部临时缓存
				window.location.href = '/'; // 去登录页
				ElMessageBox.alert('你已被登出,请重新登录', '提示', {})
					.then(() => {})
					.catch(() => {});
			}
			return Promise.reject(request.interceptors.response);
		} else {
			return response.data;
		}
	},
	(error) => {
		// 对响应错误做点什么
		if (error.message.indexOf('timeout') != -1) {
			ElMessage.error('网络超时');
		} else if (error.message == 'Network Error') {
			ElMessage.error('网络连接错误');
		} else {
			if (error.response.data) ElMessage.error(error.response.statusText);
			else ElMessage.error('接口路径找不到');
		}
		return Promise.reject(error);
	}
);

// 导出 axios 实例
export default request;

第二种

import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'

let refreshTokenCall

// create an axios instance
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
})

// request interceptor
service.interceptors.request.use(
  config => {
    // do something before request is sent
    if (config.url.includes('refresh-token')) {
      return config
    }

    if (store.getters.token == null) {
      return config
    }

    const { access_token, expires_in } = getToken()

    if (store.getters.token && expires_in > new Date()) {
      config.headers['Authorization'] = `Bearer ${access_token}`
      return config
    }

    // refresh the token if it's expired
    if (store.getters.token && expires_in < new Date()) {
      if (!refreshTokenCall) {
        refreshTokenCall = store.dispatch('user/refreshToken')
          .then((res) => {
            config.headers['Authorization'] = `Bearer ${res.access_token}`
            refreshTokenCall = undefined
          })
      }

      return refreshTokenCall.then(() => {
        return config
      })
    }
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

// response interceptor
service.interceptors.response.use(
  /**
   * If you want to get http information such as headers or status
   * Please return  response => response
  */

  /**
   * Determine the request status by custom code
   * Here is just an example
   * You can also judge the status by HTTP Status Code
   */
  response => {
    const res = response.data
    const contentType = response.headers['content-type']
    if (contentType !== 'application/xml') {
      // if the custom code is not 20000, it is judged as an error.
      if (res.code !== 20000 && res.code !== 200) {
        Message({
          message: res.message || 'Error',
          type: 'error',
          duration: 5 * 1000
        })

        // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
        if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
          // to re-login
          MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
            confirmButtonText: 'Re-Login',
            cancelButtonText: 'Cancel',
            type: 'warning'
          }).then(() => {
            store.dispatch('user/resetToken').then(() => {
              location.reload()
            })
          })
        }
        return Promise.reject(new Error(res.message || 'Error'))
      } else {
        return res
      }
    } else {
      return res
    }
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })

    if (error.response && error.response.code && error.response.code === 401) {
      // to re-login
      MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
        confirmButtonText: 'Re-Login',
        cancelButtonText: 'Cancel',
        type: 'warning'
      }).then(() => {
        store.dispatch('user/resetToken').then(() => {
          location.reload()
        })
      })
    }
    return Promise.reject(error)
  }
)

export default service

import Cookies from 'js-cookie'

const TokenKey = 'Admin-Token'

export function getToken() {
  const token = Cookies.get(TokenKey)
  return token ? JSON.parse(token) : null
}

export function getAccessToken() {
  const token = Cookies.get(TokenKey)
  return token ? JSON.parse(token).access_token : null
}

export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}

第三种

// /src/request/index.js

// /src/request/index.js

import axios from "axios";
import Qs from "qs";
import { addPendingRequest, removePendingRequest, Download } from "./method.js";
import { codeMessage } from "./constant.js";
import { history } from "../router";
import { notification } from "antd";

const request = axios.create({
  timeout: 5000,
  // 对params进行序列化,delete 和 get 请求就可以传递数组类型的参数
  paramsSerializer: function (params) {
    // indices: false 传入 ids: [1, 2, 3] 体现形式:ids=1&ids=2
    // arrayFormat: 'brackets' 传入 ids: [1, 2, 3] 体现形式:ids[]=1&ids[]=2&ids[]=3
    // arrayFormat: 'repeat' 传入 ids: [1, 2, 3] 体现形式: ids=1&ids=2&ids=3
    // 此项配置 体现形式 ids[0]=1&ids[1]=2
    return Qs.stringify(params, { arrayFormat: "repeat" });
  },
  withCredentials: true,
});

// 此处判断用来更改基础 url前缀
console.log(process.env.NODE_ENV);
if (process.env.NODE_ENV === "production") {
  // 生产环境 production
  request.defaults.baseURL = process.env.REACT_APP_BASE_URL || "/save/api/";
} else if (process.env.NODE_ENV === "test") {
  // 测试环境 test
  request.defaults.baseURL = process.env.REACT_APP_BASE_URL || "/test/api/";
} else {
  // 开发环境 development
  request.defaults.baseURL = "https://mock.mengxuegu.com/mock/6136f5af5c91b95c0946d275/api";;
}

// 请求拦截器
request.interceptors.request.use(
  (config) => {
    // 每次发送请求之前判断是否存在token,如果存在,则统一在request请求的header都加上token,不用每次请求都手动添加了
    // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
    // const token = store.state.token;
    // token && (config.headers.Authorization = token);

    if (sessionStorage.getItem("Authorization")) {
      config.headers.Authorization = sessionStorage.getItem("Authorization");
    }
   // if (["post", "put"].includes(config.method.toLocaleLowerCase())) {
      //   // 参数统一处理,请求都使用data传参
      //   config.data = config.data.data;
      // } else if (["get", "delete"].includes(config.method.toLocaleLowerCase())) {
      //   // 参数统一处理
      //   config.params = config.data;
      //   delete config.data;
      // } else {
      //   alert("不允许的请求方法:" + config.method);
      // }
    // 根据请求方式更改请求头
    config.headers = Object.assign(
      config.method.toLocaleLowerCase() === "get"
        ? {
          Accept: "application/json",
          "Content-Type": "application/json; charset=UTF-8",
        }
        : {
          "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
          // "Content-Type": "application/json; charset=UTF-8",
          // "x-csrf-token": getCookie("csrfToken")
        },
      config.headers
    );
    removePendingRequest(config);
    addPendingRequest(config);
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// 响应拦截
request.interceptors.response.use(
  async (response) => {
    removePendingRequest(response.config);
    const { data, code, message } = response.data || {};
    if (response && response.data) {
      if (response.data instanceof Blob) {
        const down = new Download(response);
        const { code, message } = await down.inspect();
        if (code === 200) {
          down.xlsx();
          return { code, message };
        }
        notification.error({
          message: "下载失败",
          description: code + ":" + message,
        });
        return { code, message };
      } else {
        // 此处做一个接口成功响应 但操作失败全局提示
        if (code !== 200) {
          notification.error({
            message: "操作失败",
          });
        }
      }
    }
    return {
      data: data || undefined, // 这里之所以用undefined,作为默认值是因为后端可能会返回为null,而null是不能给默认值的,避免成功后我们在数据处理代码中将result赋值对应类型值后直接使用方法造成报错
      code,
      message,
    };
  },
  (error) => {
    const { data, status } = error.response || {};
    const { message } = data || {};
    console.log(message);
    removePendingRequest(error.config || {});
    if (axios.isCancel(error)) {
      console.log("已取消的重复请求: " + error.message);
    } else {
      if (status === 401) {
        notification.error({
          message: "授权过期",
          description: "请重新登录!",
        });
        sessionStorage.removeItem("Authorization");
        sessionStorage.removeItem("userId");
        //重新请求逻辑....
        //store.dispatch('user/resetToken').then(() => {
          //location.reload()
        //})
        history.replace("/login");
        // window.location.href = "/login"; // 此项会造成页面刷新 不要使用
      } else {
        notification.error({
          message: status,
          description: status + ":" + codeMessage[status],
        });
      }
    }
    return Promise.reject(error);
  }
);

export default request;

//.method.js

/*
 * @Descripttion:
 * @Date: 2022-03-16 10:16:43
 */
// /src/http/tool.method.js

import Qs from "qs";
import axios from "axios";

/**
 * 获取cookie 上的csrfToken
 * @param name {string} 要获取的参数名
 * */
export function getCookie(name) {
  const str = document.cookie.split("; ");
  for (let i = 0; i < str.length; i++) {
    const temp = str[i].split("=");
    if (temp[0] === name) return unescape(temp[1]);
  }
  return "";
}

/**
 * 请求记录map
 * @type {Map}
 */
const pendingRequest = new Map();
/**
 * 用于把当前请求信息添加到pendingRequest对象中
 * @param config {object} 请求对象
 */
export function addPendingRequest(config) {
  const requestKey = generateReqKey(config);
  config.cancelToken =
    config.cancelToken ||
    new axios.CancelToken((cancel) => {
      if (!pendingRequest.has(requestKey)) {
        pendingRequest.set(requestKey, cancel);
      }
    });
}

/**
 * 用于根据当前请求的信息,生成请求 Key
 * @param config {object} 请求对象
 */
export function generateReqKey(config) {
  const { method, url, params, data } = config;
  return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
}

/**
 * 删除请求
 * @param config {object} 请求对象
 */
export function removePendingRequest(config) {
  const requestKey = generateReqKey(config);
  if (pendingRequest.has(requestKey)) {
    const cancelToken = pendingRequest.get(requestKey);
    cancelToken(requestKey);
    pendingRequest.delete(requestKey);
  }
}

/**
 * 下载类
 * setName 根据请求体自动设置文件名 如 需要自定义 请直接使用 实列filename赋值
 * zip 压缩包 传入blob文件流 完成后自动执行下载方法
 * xlsx exel表格文件 传入blob文件流 完成后自动执行下载方法
 * down 下载方法
 *
 */
export class Download {
  constructor(response) {
    this.blob = {};
    this.filename = "";
    this.response = response;
  }

  /**
   * 检测是否为 文件流
   * @param {Object|Blob} [responseData]
   */
  inspect(responseData) {
    return new Promise((resolve) => {
      let { data } = this.response || {};
      if (!data && responseData) {
        data = responseData;
      } else if (!data && !responseData) {
        resolve({
          code: 600,
          message: "检测到必要变量不存在",
        });
      }
      let file = new FileReader();
      file.onload = function (event) {
        const result = event.target.result;
        try {
          const value = JSON.parse(result);
          resolve(value);
        } catch (e) {
          // 此处表示 当前为数据流
          resolve({
            code: 200,
            message: "下载成功",
          });
        }
      };
      file.readAsText(data);
    });
  }

  /**
   * 默认自动设置文件名 需要传一个备选文件名;
   * @param {Object} [request] 请求体
   * @param {string} [customName] 备选文件名 默认值新文件
   */
  autoFileName(request, customName = "新文件") {
    let disposition = null;
    if (this.response && !request) {
      disposition = this.response.headers
        ? this.response.headers["content-disposition"]
        : null;
    } else if (request && !this.response) {
      disposition = request.headers
        ? request.headers["content-disposition"]
        : null;
    }

    if (disposition) {
      let name = disposition.split("=")[1];
      if (name) return (this.filename = decodeURI(name));
      this.filename = customName + new Date().getTime();
    }
  }

  /**
   * 压缩包
   * @param {Blob} [blobData] 文件流
   */
  zip(blobData) {
    let { data } = this.response || {};
    if (!data && blobData) {
      data = blobData;
    }
    this.blob = new Blob([data], { type: "zip;charset=utf-8" });
    this.autoFileName();
    this.down();
  }

  /**
   * xlsx 文件格式
   * @param {Blob} [blobData] 文件流
   */
  xlsx(blobData) {
    let { data } = this.response || {};
    if (!data && blobData) {
      data = blobData;
    }
    this.blob = new Blob([data], { type: "application/vnd.ms-excel" });
    this.autoFileName();
    this.down();
  }

  /**
   * 下载 最终调用
   */
  down() {
    let url = window.URL.createObjectURL(this.blob);
    let link = document.createElement("a");
    link.style.display = "none";
    link.href = url;
    link.download = `${this.filename}`;
    document.body.appendChild(link);
    link.click();
    window.URL.revokeObjectURL(link.href);
    document.body.removeChild(link);
  }
}

//constant.js
export const codeMessage = {
  200: '服务器成功返回请求的数据。',
  201: '新建或修改数据成功。',
  202: '一个请求已经进入后台排队(异步任务)。',
  204: '删除数据成功。',
  400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
  401: '用户没有权限(令牌、用户名、密码错误)。',
  403: '用户得到授权,但是访问是被禁止的。',
  404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
  406: '请求的格式不可得。',
  410: '请求的资源被永久删除,且不会再得到的。',
  422: '当创建一个对象时,发生一个验证错误。',
  500: '服务器发生错误,请检查服务器。',
  502: '网关错误。',
  503: '服务不可用,服务器暂时过载或维护。',
  504: '网关超时。',
};

第四种

/**axios封装
 * 请求拦截、相应拦截、错误统一处理
 */
import axios from 'axios';import QS from 'qs';
import { Toast } from 'vant';
import store from '../store/index'

// 环境的切换
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/';
}

// 请求超时时间
axios.defaults.timeout = 10000;

// post请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';

// 请求拦截器
axios.interceptors.request.use(    
    config => {
        // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
        // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
        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);        
        }    
    },
    // 服务器状态码不是200的情况    
    error => {        
        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);        
        }       
    }
);
/** 
 * get方法,对应get请求 
 * @param {String} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */
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方法,对应post请求 
 * @param {String} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */
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)        
        })    
    });
}

个人笔记

你可能感兴趣的:(vue,前端,javascript,vue.js)