开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第二天,点击查看活动详情
题引:
公司终于决定把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进行处理的一个流程。
暂无评论内容