2
0
mirror of https://github.com/tenrok/axios.git synced 2026-06-17 19:21:29 +03:00

Fixed toFormData regression bug (unreleased) with Array-like objects serialization; (#4714)

Added `toURLEncodedForm` helper;
Added automatic payload serialization to `application/x-www-form-urlencoded` to have parity with `multipart/form-data`;
Added test of handling `application/x-www-form-urlencoded` body by express.js;
Updated README.md;
Added missed param in JSDoc;
Fixed hrefs in README.md;

Co-authored-by: Jay <jasonsaayman@gmail.com>
This commit is contained in:
Dmitriy Mozgovoy
2022-05-16 09:30:17 +03:00
committed by GitHub
parent e762cf77b3
commit c05ad48952
13 changed files with 245 additions and 65 deletions
+19 -10
View File
@@ -5,6 +5,7 @@ var normalizeHeaderName = require('../helpers/normalizeHeaderName');
var AxiosError = require('../core/AxiosError');
var transitionalDefaults = require('./transitional');
var toFormData = require('../helpers/toFormData');
var toURLEncodedForm = require('../helpers/toURLEncodedForm');
var DEFAULT_CONTENT_TYPE = {
'Content-Type': 'application/x-www-form-urlencoded'
@@ -71,18 +72,26 @@ var defaults = {
}
var isObjectPayload = utils.isObject(data);
var contentType = headers && headers['Content-Type'];
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(),
this.formSerializer
);
} else if (isObjectPayload || contentType === 'application/json') {
if (isObjectPayload) {
if (contentType.indexOf('application/x-www-form-urlencoded') !== -1) {
return toURLEncodedForm(data).toString();
}
if ((isFileList = utils.isFileList(data)) || contentType.indexOf('multipart/form-data') !== -1) {
var _FormData = this.env && this.env.FormData;
return toFormData(
isFileList ? {'files[]': data} : data,
_FormData && new _FormData(),
this.formSerializer
);
}
}
if (isObjectPayload || contentType.indexOf('application/json') !== -1) {
setContentTypeIfUnset(headers, 'application/json');
return stringifySafely(data);
}
+1
View File
@@ -17,6 +17,7 @@ function encode(val) {
*
* @param {string} url The base of the url (e.g., http://www.google.com)
* @param {object} [params] The params to be appended
* @param {?object} paramsSerializer
* @returns {string} The formatted url
*/
module.exports = function buildURL(url, params, paramsSerializer) {
+22 -20
View File
@@ -20,6 +20,20 @@ function renderKey(path, key, dots) {
}).join(dots ? '.' : '');
}
function convertValue(value) {
if (value === null) return '';
if (utils.isDate(value)) {
return value.toISOString();
}
if (utils.isArrayBuffer(value) || utils.isTypedArray(value)) {
return typeof Blob === 'function' ? new Blob([value]) : Buffer.from(value);
}
return value;
}
function isFlatArray(arr) {
return utils.isArray(arr) && !arr.some(isVisitable);
}
@@ -64,21 +78,6 @@ function toFormData(obj, formData, options) {
throw new TypeError('visitor must be a function');
}
function convertValue(value) {
if (value === null) return '';
if (utils.isDate(value)) {
return value.toISOString();
}
if (utils.isArrayBuffer(value) || utils.isTypedArray(value)) {
return typeof Blob === 'function' ? new Blob([value]) : Buffer.from(value);
}
return value;
}
/**
*
* @param {*} value
@@ -88,7 +87,7 @@ function toFormData(obj, formData, options) {
* @returns {boolean} return true to visit the each prop of the value recursively
*/
function defaultVisitor(value, key, path) {
var arr;
var arr = value;
if (value && !path && typeof value === 'object') {
if (utils.endsWith(key, '{}')) {
@@ -96,7 +95,10 @@ function toFormData(obj, formData, options) {
key = metaTokens ? key : key.slice(0, -2);
// eslint-disable-next-line no-param-reassign
value = JSON.stringify(value);
} else if (!utils.isPlainObject(value) && (arr = utils.toArray(value)) && isFlatArray(arr)) {
} else if (
(utils.isArray(value) && isFlatArray(value)) ||
(utils.isFileList(value) || utils.endsWith(key, '[]') && (arr = utils.toArray(value))
)) {
// eslint-disable-next-line no-param-reassign
key = removeBrackets(key);
@@ -138,7 +140,7 @@ function toFormData(obj, formData, options) {
stack.push(value);
utils.forEach(value, function each(el, key) {
var result = !utils.isUndefined(el) && defaultVisitor.call(
var result = !utils.isUndefined(el) && visitor.call(
formData, el, utils.isString(key) ? key.trim() : key, path, exposedHelpers
);
@@ -150,8 +152,8 @@ function toFormData(obj, formData, options) {
stack.pop();
}
if (!utils.isPlainObject(obj)) {
throw new TypeError('data must be a plain object');
if (!utils.isObject(obj)) {
throw new TypeError('data must be an object');
}
build(obj);
+18
View File
@@ -0,0 +1,18 @@
'use strict';
var utils = require('../utils');
var toFormData = require('./toFormData');
var platform = require('../platform/');
module.exports = function toURLEncodedForm(data) {
return toFormData(data, new platform.classes.URLSearchParams(), {
visitor: function(value, key, path, helpers) {
if (platform.isNode && utils.isBuffer(value)) {
this.append(key, value.toString('base64'));
return false;
}
return helpers.defaultVisitor.apply(this, arguments);
}
});
};
@@ -0,0 +1,38 @@
'use strict';
module.exports = (function getURLSearchParams(nativeURLSearchParams) {
if (typeof nativeURLSearchParams === 'function') return nativeURLSearchParams;
function encode(str) {
var charMap = {
'!': '%21',
"'": '%27',
'(': '%28',
')': '%29',
'~': '%7E',
'%20': '+',
'%00': '\x00'
};
return encodeURIComponent(str).replace(/[!'\(\)~]|%20|%00/g, function replacer(match) {
return charMap[match];
});
}
function URLSearchParams() {
this.pairs = [];
}
var prototype = URLSearchParams.prototype;
prototype.append = function append(name, value) {
this.pairs.push([name, value]);
};
prototype.toString = function toString() {
return this.pairs.map(function each(pair) {
return pair[0] + '=' + encode(pair[1]);
}, '').join('&');
};
return URLSearchParams;
})(URLSearchParams);
+8
View File
@@ -0,0 +1,8 @@
'use strict';
module.exports = {
isBrowser: true,
classes: {
URLSearchParams: require('./classes/URLSearchParams')
}
};
+3
View File
@@ -0,0 +1,3 @@
'use strict';
module.exports = require('./node/');
@@ -0,0 +1,5 @@
'use strict';
var url = require('url');
module.exports = url.URLSearchParams;
+8
View File
@@ -0,0 +1,8 @@
'use strict';
module.exports = {
isNode: true,
classes: {
URLSearchParams: require('./classes/URLSearchParams')
}
};