axios的get、post、文件上传、下载封装

4,841 阅读2分钟

该文只讲封装,不涉及下载安装原理讲解。以下所有的封装代码需要写在一个文件内并将其暴露,并在写入 API 的文件内引入。

创建http请求

利用axios创建一个实例:

const baseURL = `/${process.env.VUE_APP_API_PREFIX}`; // 接口基本路径

const http = axios.create({
  timeout: timeOut,
  withCredentials: true,
  headers: { 'X-Requested-With': 'XMLHttpRequest', 'X-CSRF-TOKEN': getToken() },
  baseURL
});

withCredentials: true 在发起跨域请i去的时候,后端已经开启CORS,前端需要也携带cookie,此时需要前端的请求头加上此配置,表示请求可以携带cookie。

封装get、post请求

export default{
  get(opts) {
    return new Promise((resolve, reject) => {
      const params = {
        method: 'get'
      };
      http(merge(params, opts))
        .then(res => {
          // 这里就写业务逻辑请求时拿到的res,一般包含data、code、msg、res等
          resolve(res);
        })
        .catch(err => {
          reject(err);
        });
    });
  },
  post(opts) {
    return new Promise((resolve, reject) => {
      const params = {
        method: 'post',
        headers: {
          'Content-Type': 'application/json'
        }
      };
      http(merge(params, opts))
        .then(res => {
          // 这里就写业务逻辑请求时拿到的res,一般包含data、code、msg、res等
          resolve(res);
        })
        .catch(err => {
          reject(err);
        });
    });
  },
}

使用封装好的get、post去请求服务器:

// 普通get请求
function getComponentByIdentification(params) {
  return http.get({
    url: `/web/system/getComponentByIdentification`,
    params
  });
}
// get文件下载,此是需要在请求到接口后进行处理blob文件进行创建a标签进行下载
function downloadFile(params) {
  return http.get({
    url: '/web/protocol/download',
    responseType: 'blob',
    params
  });
}
// 普通post请求
function saveOrUpdateProtocol(params) {
  return http.post({
    url: 'web/protocolManager/saveOrUpdateProtocol',
    data: params
  });
}

用法

// get、post请求写法
const param = {
	protocolBaseInfo: this.protocolBaseInfo
};
getComponentByIdentification(param)
	.then(res => {
    // 写业务逻辑
})
// 不传参则直接写
getComponentByIdentification()
    .then(res => {
    // 写业务逻辑
})

封装download下载请求,下载请求与get、post不同,需要兼容IE、以及判断下载的是哪种类型的文件。

以下代码对下载excel文件进行了封装,并且对下载失败的情况进行封装。

前端向服务端请求下载文件是以流的格式(stream)传递到前端,前端通常是将流转换为objectURL,借用a标签的download属性,进行文件下载。

文件下载失败时,服务端消息返回的文件格式不再是流,而是服务端返回的消息json(errorCode之类)对象,但是依然被包裹再 Blob 类型文件内,此时 blob 的 type 是text/json,前端虽然可以正常导出文件,但是文件内容是undefined。

值得注意的是

若是下载失败的情况,返回的类型是为 text/json,但是 IE 和 chrome 两者返回的不完全一样,如下:

IE 的 type 为 text/json;charset=utf-8"

chrome 为 text/json

因此 若需要利用返回文件类型为 json 还是 stream,在响应拦截器内对错误状况作出处理时 ,判断文件类型时不能写入 res.type === 'text/json',需要写为 res.type.indexOf('json') > -1

因此需要对文件内容进行解析

download(opts) {
    return new Promise((resolve, reject) => {
      let newParams = {
        method: 'get',
        responseType: 'blob'
      };
      newParams = merge(newParams, opts);
      http(newParams)
        .then(res => {
          const blob = new Blob([res]);
          // 判断接收到的文件是否为json类型,是则需要作错误处理
          if (res.type.indexOf('json') > -1) {
            var newblob = blob.slice(0, blob.size);
             // 需要对包裹在blob文件内部的json文件解析出来
            var reader = new FileReader();
            reader.readAsText(newblob, 'utf-8');
            reader.onload = function(evt) {
              if (evt.target.readyState === FileReader.DONE) {
                const result = JSON.parse(evt.target.result);
                // 此时的result 和get、post接口一样返回的json对象内包含data、msg、code等,根据此					可在此处作出一些错误处理(弹出弹框等)
              }
            };
          } else {
            // execl文件
            const { params } = newParams;
            if (params.ext) params.type = params.ext;
            if (params.type === 'excel') params.type = 'xlsx';
            const fileName = `${params.fileName}.${params.type}`;
            if ('download' in document.createElement('a')) {
              // 非IE下载
              const elink = document.createElement('a');
              elink.download = fileName;
              elink.style.display = 'none';
              elink.href = URL.createObjectURL(blob);
              document.body.appendChild(elink);
              elink.click();
              URL.revokeObjectURL(elink.href); // 释放URL 对象
              document.body.removeChild(elink);
            } else {
              /** IE10+下载**/
              navigator.msSaveBlob(blob, fileName);
            }
          }
          resolve(res);
        })
        .catch(err => {
          reject(err);
        });
    });
  }

利用此进行文件下载:

// 文件下载,请求到数据后不需要进行任何处理,由封装好的download处理
function downloadFile(params) {
  return http.download({
    url: '/web/protocol/download',
    params
  });
}

上传请求封装

upload(opts) {
    opts.url = `${opts.url}.do`;
    if (getToken() !== '') {
      opts.url += `?_csrf=${getToken()}`;
    }
    const axiosUpload = axios.create({
      baseURL,
      timeout: timeOut,
      headers: {
        'Content-Type': 'multipart/form-data',
        'X-Requested-With': 'XMLHttpRequest'
      }
    });
    // 响应拦截器
    axiosUpload.interceptors.response.use(function(response) {
      // 对错误进行统一处理
      return Promise.resolve(response.data);
    });
    return new Promise((resolve, reject) => {
      axiosUpload
        .post(opts.url, opts.data)
        .then(res => {
          resolve(res);
        })
        .catch(err => {
          reject(err);
        });
    });
  },

这里的'X-Requested-With': 'XMLHttpRequest'是让服务端知晓本次请求是ajax请求,看组件业务需求是否需要设置,公司业务是需要和单点登录对接跳转其他链接所用。

利用此进行上传文档

// 文档上传
function uploadFile(file) {
  return http.upload({
    url: `/web/protocolFile/upload`,
    data: file
  });
}

// 调用接口时
const form = new FormData();
// 文件对象
form.append('file', file);
// 请求方法
uploadFile(form)
	.then(res => {
    // 写业务逻辑
})

响应拦截

对于响应拦截,普通的get、post只需要根据错误码弹出错误框即可;但是对于上传、下载接口需要特殊处理。

http.interceptors.response.use(
  function(response) {
    // 请求文件流 使用get请求下载并且接口返回正常的情况
    if (response.data.type === 'application/octet-stream') {
      // console.log('文件流');
      return response;
    }
      
    // 拦截下载接口 如果下载接口使用get请求,需要判断是否为错误的状况
    // 错误则需要将blob文件解析,然后赋值给response.data,可以接着走以下的错误处理逻辑
    if (response.data.type && response.data.type.indexOf('json') > -1) {
      var reader = new FileReader();
      reader.addEventListener('loadend', function(e) {
        response.data = JSON.parse(e.target.result);
      });
      reader.readAsText(response.data);
    }
      
    // 对错误进行统一处理
    if (response.data.code !== '0') {
      console.log('cuowule!!');
      // 后台返回错误,可配合业务组件的一些信息作一些错误处理
    }
    // response.data是接口请求完成后.then内拿到的真正的数据,不包含header等
    return Promise.resolve(response.data);
  },
  function(error) {
    const response = error.response;
    const rp = response.status;
   
    if (rp === 401 || rp === 403 || rp === 302) {
      if (process.env.NODE_ENV !== 'development') {
        window.location.reload();
      }
    } else {
      if (error.message.indexOf('timeout') > -1) {
        // 超时判断,需要弹出超时的弹出框
      } else {
        // 接口错误,做错误处理
      }
    }
    // 对响应错误做点什么
    return Promise.reject(error);
  }
);

请求拦截

// 请求拦截器
http.interceptors.request.use(function (config) {
  if (config.method === 'get') {
    config.url.indexOf('?') > '-1' ? 
        config.url = config.url + '&_=' + Date.now() 
    	: config.url = config.url + '?_=' + Date.now()
  }
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})