axios内部的数据转换方法
题引:
公司终于决定把vue2的项目升级到vue3,那就少不了配置文件和封装。 在axios的二次封装中,对post请求的参数通过算法进行加密处理成了36位的字符串。是的,字符串,就是因为data被处理成了字符串类型传递过去,且在axios^0.27的新版本里,默认的transformRequest方法对data是不是对象类型进行了更丰富的判断,下面开始讲解一下。
正文:
在报错的情况下通过调试,一步一步的寻找问题,后面定位了问题的所在,顺便贴出axios的执行流程。
整体流程:
axios/axios.create() -> request(config) -> request interceptors -> dispatchRequest(config) -> xhrAdapter(config) -> resolve()/reject() -> response interceptors -> 请求的onResolve或onRejectd
函数分析:
request(config)
将请求拦截器 / dispatchRequest() / 响应拦截器 通过 promise 串连起来, 返回 promise。
dispatchRequest(config)
转换请求数据 ===> 调用 xhrAdapter()发请求 ===> 请求返回后转换响应数据. 返回 promise。
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); } 复制代码
公司的问题就是出现在这里。
一开始传递的参数是对象,但是在请求拦截器那里被加密变成了字符串,且contentType是application/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