• home > webfront > ECMAS > vue >

    再谈from属性EncType与axios封装—axios拦截与中断—源码浅析

    Author:zhoulujun Date:

    ajax拦截器是必须根据项目设计的,loading处理,http状态码处理,私有http头,数据加盐。本文从代码浅解Axios拦截器配置。如何中断ajax或者axios的异步请求?interceptors,CancelToken源码分析、应用案列

    若干年前使用angular的时候,总结过《from属性EncType提交数据的格式详解—在angular中的应用

    在远古ie兴盛的时代,基本是fromData提交数据,ajax+jquery推动了时代的变革,当数据提交也大抵如此,直到angularJS到来,前后端才通用json形式,转眼从react跳转到vue(感觉被Vue强暴),闲话打住

    axios.CancelToken

    cancelToken可以取消axios请求,一般原生XMLHttpRequest ajax请求的话,用的是abort()这个方法。

    推荐阅读文章:Vue切换页面时中断axios请求axios 之cancelToken原理以及使用

    var CancelToken = axios.CancelToken;
    var source = CancelToken.source();
    axios.get('/user/12345', {
        //get请求在第二个参数,post请求在第三个参数
        cancelToken: source.token
    })
    source.cancel('不想请求了');

    get请求的时候,cancelToken是放在第二个参数里;post的时候,cancelToken是放在第三个参数里。

    实现原理大致如下:

    let resolveHandle;
    new Promise((resolve)=>{
        resolveHandle=resolve;
    }).then((val)=>{
        console.log('resolve',val);
    });
    resolveHandle('ok');

    上面的例子中,我们用resolveHandle获取了一个promise的resolve方法的控制权,这样,我们就可以在外部控制这个promise的成功了。要知道new Promise返回的对象是无法从外部决定它成功还是失败的。axios中断请求方法也是劫持,具体看起源码。

    axios.interceptors

    理论上,一套api接口,一般统一json格式,要么统一fromData,但是,就是有不按套路出牌(后端屌啊),三个都有

    application/x-www-form-urlencoded

    multipart/form-data

    application/json

    同时,接口返回数据,理论上应该统一规划,比如消息属性(toast统一处理),数据分层,但是,泪崩

    axios封装是必须,但是,前端时候也得考虑下规范(架构设计)方面的问题——小白秉性暴露无遗

    看了axios源码

    +axios

        index.js

        index.d.ts

        +lib

           axios.js

           +core

              Axios.js

    axios本身就会检测数据,匹配不同Content-Type

    transformRequest: [function transformRequest(data, headers) {
      normalizeHeaderName(headers, 'Content-Type');
      if (utils.isFormData(data) ||
        utils.isArrayBuffer(data) ||
        utils.isBuffer(data) ||
        utils.isStream(data) ||
        utils.isFile(data) ||
        utils.isBlob(data)
      ) {
        return data;
      }
      if (utils.isArrayBufferView(data)) {
        return data.buffer;
      }
      if (utils.isURLSearchParams(data)) {
        setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
        return data.toString();
      }
      if (utils.isObject(data)) {
        setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
        return JSON.stringify(data);
      }
      return data;
    }],

    但是,实际提交都是 Object类型,于是就得转

    第一种处理模式,就是prototype大法,

    axios.postForm = function (url, data, config = null) {
      return new Promise(function (resolve, reject) {
        config = {
          method: 'post',
          url: url,
          data: () => {
            let fromData = new FormData();
            for (let i in data) {
              fromData.append(i,data[i])
            }
            return fromData;
          }
        };
        axios.request(config).then((response) => {
          resolve(response)
        }, err => {
          reject(err);
        })
      })
    };

    第二种,重新构造一个新的函数,call回调,或者

    class http {
      constructor(config) {
        if (config) {
          return new Promise((resolve, reject) => {
            axios(config)
              .then(function (response) {
    
              });
          });
        }
      }
      static get(url, parma) {}
      static post(url, param) {}
    }


    发现在var axios= Create an instance of Axios,axios get post request 都是集中处理

    Axios.prototype.request

    // Provide aliases for supported request methods
    utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
      /*eslint func-names:0*/
      Axios.prototype[method] = function(url, config) {
        return this.request(utils.merge(config || {}, {
          method: method,
          url: url
        }));
      };
    });
    
    utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
      /*eslint func-names:0*/
      Axios.prototype[method] = function(url, data, config) {
        return this.request(utils.merge(config || {}, {
          method: method,
          url: url,
          data: data
        }));
      };
    });

    于是决定,通过增加一个属性声明,如:commitType:'form'

    于是,就会有如下代码


    import axios from 'axios';
    import queryString from 'queryString';//nodeJs内置,无需npm i
    axios.interceptors.request.use(function (config) {
      //TODO 请求拦截
      /*store.commit('updateLoadingStatus', {isLoading: true});*/
      if (config.commitType) {
        if (config.commitType === 'form') {
          config.headers['Content-Type'] = 'application/x-www-form-urlencoded';
          config.transformRequest = [function (data) {
            return queryString.stringify(data);//利用对应方法转换格式
          }]
        } else if (config.commitType === 'url') {
          config.headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
          config.transformRequest = [function (data) {
            return decodeURIComponent(data);
          }];
        }
      }
    
      console.log(config);
      return config;
    }, function (error) {
      //请求错误时做些事
      return Promise.reject(error);
    });
    
    //响应拦截器即异常处理
    axios.interceptors.response.use(response => {
      //TODO 统一处理逻辑
      //store.commit('updateLoadingStatus', {isLoading: false});
      if (response.data) {
        return response.data;
      }
      return response
    }, err => {
      if (err && err.response) {
        //http状态码处理
        switch (err.response.status) {
          case 400:
            err.msg = '错误请求';
            break;
          case 401:
            err.msg = '未授权,请重新登录';
            break;
          case 403:
            err.msg = '拒绝访问';
            break;
          case 404:
            err.msg = '请求错误,未找到该资源';
            break;
          case 405:
            err.msg = '请求方法未允许';
            break;
          case 408:
            err.msg = '请求超时';
            break;
          case 500:
            err.msg = '服务器端出错';
            break;
          case 501:
            err.msg = '网络未实现';
            break;
          case 502:
            err.msg = '网络错误';
            break;
          case 503:
            err.msg = '服务不可用';
            break;
          case 504:
            err.msg = '网络超时';
            break;
          case 505:
            err.msg = 'http版本不支持该请求';
            break;
          default:
            err.msg = `连接错误${err.response.status}`
        }
      } else {
        err.msg = '连接到服务器失败';
      }
      return Promise.resolve(err.response)
    });
    //超时时间
    axios.defaults.timeout = 5000;
    
    export default axios;

    拓展文章:

    Vue中axios的使用技巧配置项详解

    Vue基于vuex、axios拦截器实现loading效果及axios的安装配置



    转载本站文章《再谈from属性EncType与axios封装—axios拦截与中断—源码浅析》,
    请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/vue/8137.html