基于axios封装的http请求

http模块整体目录结构

src
|   
-- http 封装axios模块文件夹
    |
    ---- config.js          axios的默认配置
    ---- api.js             二次封装axios,报错、权限验证、跳转、拦截、提示等
    ---- index.js           将axios封装成插件
    ---- modules            请求接口文件目录
           |
           ---- index.js    自动化处理文件:自动引入接口的核心文件
           ---- home.js     接口1
           ---- news.js     接口2
复制代码

1、配置信息--config.js

import {
  baseUrl
} from '../config/urls.js'

/**
 * 默认配置
 */
export default {
  // 基础url前缀
  baseURL: baseUrl,
  // 请求头信息
  headers: {
    'Content-Type': 'application/json;charset=UTF-8',
    'Accept': 'application/json'
  },
  // 设置超时时间
  timeout: 10000,
  // 携带凭证
  // 
  withCredentials: false,
  // 返回数据类型
  responseType: 'json',
}

复制代码

2、api模块--api.js

import axios from 'axios' // 注意先安装哦
import qs from 'qs' // 序列化请求数据,视服务端的要求
import config from './config.js' // 导入入默认配置

import store from '@/store/'

let cancel, promiseArr = {}
const CancelToken = axios.CancelToken;
//配置全局取消数组
window.__axiosPromiseArr = [];

const instance = axios.create(config)

// request 拦截器
instance.interceptors.request.use(
  request => {
 //********1、发起请求时,取消掉当前正在进行的相同请求************
    if (promiseArr[request.url]) {
      promiseArr[request.url]('操作取消')
      promiseArr[request.url] = cancel
    } else {
      promiseArr[request.url] = cancel
    }
    
//********2、单页应用中在切换页面时应该终止之前的请求!!!!!***********
    // Tip: 1
    // 请求开始的时候可以结合 vuex 开启全屏的 loading 动画
    store.commit('SET_LOADING', true);

    // Tip: 2 权限验证
    // 带上 token , 可以结合 vuex 或者重 localStorage
    // if (store.getters.token) {
    // request.headers['X-Token'] = getToken() // 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改
    // } else {
    // // 重定向到登录页面
    // }

    // Tip: 3
    // 根据请求方法,序列化传来的参数,根据后端需求是否序列化
    if (request.method.toLocaleLowerCase() === 'post' ||
      request.method.toLocaleLowerCase() === 'put' ||
      request.method.toLocaleLowerCase() === 'delete') {

      request.data = qs.stringify(request.data)
    }
    return request
  },
  error => {
    console.log('request:', error)
    // 请求错误时做些事(接口错误、超时等)
    // Tip: 4
    // 关闭loadding
    store.commit('SET_LOADING', false);

    // 1.判断请求超时
    if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {
      console.log('根据你设置的timeout/真的请求超时 判断请求现在超时了,你可以在这里加入超时的处理方案')
      // return service.request(originalRequest);//例如再重复请求一次
    }
    // 2.需要重定向到错误页面
    const errorInfo = error.response
    console.log(errorInfo)
    if (errorInfo) {
      // error =errorInfo.data//页面那边catch的时候就能拿到详细的错误信息,看最下边的Promise.reject
      const errorStatus = errorInfo.status; // 404 403 500 ... 等
      router.push({
        path: `/error/${errorStatus}`
      })
    }
    return Promise.reject(error) // 在调用的那边可以拿到(catch)你想返回的错误信息
  }
)

// response 响应拦截器
instance.interceptors.response.use(
  response => {
    // 加到时器主要是为了 展示Loading效果 项目中应去除
    setTimeout(() => {
      store.commit('SET_LOADING', false);
    }, 300)
    let data;
    // IE9时response.data是undefined,因此需要使用response.request.responseText(Stringify后的字符串)
    if (response.data == undefined) {
      data = response.request.responseText
    } else {
      data = response.data
    }
    // 根据返回的code值来做不同的处理(和后端约定)
    // switch (data.code) {
    //   case '':
    //     break;
    //   default:
    // }

    // 若不是正确的返回code,且已经登录,就抛出错误
    // const err = new Error(data.description)

    // err.data = data
    // err.response = response

    // throw err
    return data
  },
  err => {

    if (err && err.response) {
      switch (err.response.status) {
        case 400:
          err.message = '请求错误'
          break

        case 401:
          err.message = '未授权,请登录'
          break

        case 403:
          err.message = '拒绝访问'
          break

        case 404:
          err.message = `请求地址出错: ${err.response.config.url}`
          break

        case 408:
          err.message = '请求超时'
          break

        case 500:
          err.message = '服务器内部错误'
          break

        case 501:
          err.message = '服务未实现'
          break

        case 502:
          err.message = '网关错误'
          break

        case 503:
          err.message = '服务不可用'
          break

        case 504:
          err.message = '网关超时'
          break

        case 505:
          err.message = 'HTTP版本不受支持'
          break

        default:
      }
    }
    store.commit('SET_LOADING', false);
    // 对响应错误做点什么
    console.error(err)
    // 此处我使用的是 element UI 的提示组件
    // Message.error(`ERROR: ${err}`);
    return Promise.reject(err) // 返回接口返回的错误信息
  }
)

export default (url = '', data = {}, type = 'GET') => {
  let options = {
    url,
    cancelToken: new CancelToken(c => {
      cancel = c
      window.__axiosPromiseArr.push({cancel})     //放入一个全局数组,以便之后在router中统一取消
    })
  }
  type = type.toUpperCase();
  return new Promise((resolve, reject) => {
    if (type === 'GET') {
      options = Object.assign(options, {
        method: 'get',
        params: data,
      })
    } else if (type === 'POST') {
      options = Object.assign(options, {
        method: 'post',
        data,
      })
    }
    instance(options)
      .then(res => {
        resolve(res)
        return false
      })
  })
}
复制代码

3、业务模块--modules

以 news.js 模块为例:

import axios from '../api' // 导入 api

export const News = (language = '1', type = '1') => axios('/news/index', {
    p: 'wx',
    language,
    type,
});
复制代码

业务模块导出(关键部分):

modules下的index.js
/**
 * 自动化处理文件:自动引入接口的核心文件
 */
const merge = require('webpack-merge')
const files = require.context('.', true, /\.js$/)

let configInterface;
/**
 * merge interface
 */
 
console.log(files.keys()) // ["./news.js"] 返回一个数组

files.keys().forEach(key => {
  if (key === './index.js') return;
  const filesConfig = files(key)
  configInterface = merge({}, configInterface, filesConfig.default || filesConfig) // 读取出文件中的default模块
})
export default configInterface
复制代码

/*********************************************************************

这里有个很重要的函数--->require.context

/**
* @param directory 要搜索的文件夹目录不能是变量,否则在编译阶段无法定位目录
* @param useSubdirectories  是否搜索子目录
* @param regExp 匹配文件的正则表达式,匹配哪种类型的文件
* @return function 返回一个具有 resolve, keys, id 三个属性的方法
          resolve() 它返回请求被解析后得到的模块 id
          keys() 它返回一个数组,由所有符合上下文模块处理的请求组成。 
          id 是上下文模块里面所包含的模块 id. 它可能在你使用 module.hot.accept 的时候被用到
*/
require.context('demo', useSubdirectories = false, regExp = /\.js$/)
// (创建了)一个包含了 demo 文件夹(不包含子目录)下面的、所有文件名以 `js` 结尾的、能被 require 请求到的文件的上下文。

复制代码

*********************************************************************/

4、导出注入到Vue实例,供全局调用---index.js

/**
 * 导入所有接口
 */
import apiList from './modules/index'   

const install = Vue => {
  if (install.installed) return;
  install.installed = true;
  Object.defineProperties(Vue.prototype, {
    // 此处挂载在 Vue 原型的 $api 对象上
    $api: {
      get() {
        return apiList
      }
    }
  })
}
export default install
复制代码

5、main.js

//导入 http 文件夹下的 index.js
import api from './http'
Vue.use(api)
复制代码

组件中的调用方式:

this.$api.News().then((res) => {
      console.log(res)
})
复制代码

转载于:https://juejin.im/post/5c46939c6fb9a049bd429fdf

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值