阅读 80

axios内部的数据转换方法

题引:

公司终于决定把vue2的项目升级到vue3,那就少不了配置文件和封装。 在axios的二次封装中,对post请求的参数通过算法进行加密处理成了36位的字符串。是的,字符串,就是因为data被处理成了字符串类型传递过去,且在axios^0.27的新版本里,默认的transformRequest方法对data是不是对象类型进行了更丰富的判断,下面开始讲解一下。

正文:

在报错的情况下通过调试,一步一步的寻找问题,后面定位了问题的所在,顺便贴出axios的执行流程。

axios流程.png

整体流程:

  1. axios/axios.create() -> request(config) -> request interceptors -> dispatchRequest(config) -> xhrAdapter(config) -> resolve()/reject() -> response interceptors -> 请求的onResolve或onRejectd

函数分析:

  1. request(config)

    将请求拦截器 / dispatchRequest() / 响应拦截器 通过 promise 串连起来, 返回 promise。

  2. dispatchRequest(config)

    转换请求数据 ===> 调用 xhrAdapter()发请求 ===> 请求返回后转换响应数据. 返回 promise。

  3. xhrAdapter(config)

    创建 XHR 对象, 根据 config 进行相应设置, 发送特定请求, 并接收响应数据, 返回 promise、

    是的,我们通过上面的流程图,我们直接前往axios/lib/core/dispatchRequest.js,可以看到这一段代码

module.exports = function dispatchRequest(config) {   ...其他代码      // NOTE 这里就是将请求数据进行数据转换的地址入口   // Transform request data   config.data = transformData(     config.data,     config.headers,     config.transformRequest   );      ...其他代码   return adapter(config).then(function onAdapterResolution(response) {     throwIfCancellationRequested(config);          // NOTE 这里就是将响应数据进行数据转换的地址入口     // Transform response data     response.data = transformData(       response.data,       response.headers,       config.transformResponse     );          return response;   }, function onAdapterRejection(reason) {     ...其他代码   }); }; 复制代码

我们可以看到,这里有两个转换数据的函数调用,一个是请求数据、一个是响应式数据。 如果我们没有定义transformRequest或者transformResponse方法,那么axios就会采用默认的方法来执行。我们可以前往axios/lib/default.js

这是以前的vue2用的版本的默认转换方法 var defaults = {   ...其他代码   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();     }     //如果是对象,则会调用JSON.stringify方法     if (utils.isObject(data)) {       setContentTypeIfUnset(headers, 'application/json;charset=utf-8');       return JSON.stringify(data);     }     return data;   }],   ...其他代码 } vue3用的axios版本代码   transformRequest: [function transformRequest(data, headers) {     normalizeHeaderName(headers, 'Accept');     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();     }     // 这里开始就跟旧版本不一样了,旧版本是直接判断是对象就 return JSON.stringify(data);     // 新版本新增了对file类型的判断     var isObjectPayload = utils.isObject(data);     var contentType = headers && headers['Content-Type'];     var isFileList;     // 这里是文件类型的判断     if ((isFileList = utils.isFileList(data)) || (isObjectPayload && contentType === 'multipart/form-data')) {       var _FormData = this.env && this.env.FormData;       return toFormData(isFileList ? {'files[]': data} : data, _FormData && new _FormData());     }      // 这里不管 isObjectPayload 是不是true,只要类型是 pplication/json 都会触发     else if (isObjectPayload || contentType === 'application/json') {       setContentTypeIfUnset(headers, 'application/json');       return stringifySafely(data);     }     return data;   }], 复制代码

function stringifySafely(rawValue, parser, encoder) {   // 只要是字符串就会触发   if (utils.isString(rawValue)) {     try {       // 由于是字符串(字母+数字),调用JSON.parse会报错       (parser || JSON.parse)(rawValue);       return utils.trim(rawValue);     } catch (e) {       if (e.name !== 'SyntaxError') {         throw e;       }     }   }   // 所以会执行到这里 eg:JSON.stringify('32131asdas') = '"32131asdas"'    // 也就是为什么后端那边解析字符串的时候还是字符串,就会报接口错误404   return (encoder || JSON.stringify)(rawValue); } 复制代码

公司的问题就是出现在这里。
一开始传递的参数是对象,但是在请求拦截器那里被加密变成了字符串,且contentTypeapplication/json类型,最后一定会调用stringifySafely方法,变成两层引号的字符串,后端那边解析字符串的时候还是字符串,就会报接口错误。

接下来我们进入transformData函数内部看看里面的逻辑

module.exports = function transformData(data, headers, fns) {   // fns : 是转换方法   utils.forEach(fns, function transform(fn) {     data = fn(data, headers);   });   return data; }; // utils.foeEach 方法 function forEach(obj, fn) {   // obj是转换方法    // fn是回调函数   // NOTE 如果转换方法是null或者undefined 直接返回  if (obj === null || typeof obj === 'undefined') {    return;  }  // NOTE 如果不是对象 则变成数组形式  if (typeof obj !== 'object') {    /*eslint no-param-reassign:0*/    obj = [obj];  }  // NOTE 如果是数组 则执行已有的转换方法  if (isArray(obj)) {    // Iterate over array values    for (var i = 0, l = obj.length; i < l; i++) {      fn.call(null, obj[i], i, obj);    }  } else {    // Iterate over object keys    for (var key in obj) {      if (Object.prototype.hasOwnProperty.call(obj, key)) {        fn.call(null, obj[key], key, obj);      }    }  } } 复制代码

这也就是axios内部会对我们的data进行处理的一个流程。


作者:你的心上进
链接:https://juejin.cn/post/7169149443285975047

文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐