Added axios.formToJSON method; (#4735)

* Draft

* Added `formDataToJSON` helper;
Added `axios.formToJSON` method;
Added client tests;

Co-authored-by: Jay <jasonsaayman@gmail.com>
This commit is contained in:
Dmitriy Mozgovoy 2022-05-25 09:21:40 +03:00 committed by GitHub
parent 934f390cc3
commit c008e57fe4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 195 additions and 10 deletions

7
index.d.ts vendored
View File

@ -300,6 +300,12 @@ export interface GenericFormData {
append(name: string, value: any, options?: any): any;
}
export interface GenericHTMLFormElement {
name: string;
method: string;
submit(): void;
}
export interface AxiosStatic extends AxiosInstance {
create(config?: CreateAxiosDefaults): AxiosInstance;
Cancel: CancelStatic;
@ -312,6 +318,7 @@ export interface AxiosStatic extends AxiosInstance {
spread<T, R>(callback: (...args: T[]) => R): (array: T[]) => R;
isAxiosError<T = any, D = any>(payload: any): payload is AxiosError<T, D>;
toFormData(sourceObj: object, targetFormData?: GenericFormData, options?: FormSerializerOptions): GenericFormData;
formToJSON(form: GenericFormData|GenericHTMLFormElement): object;
}
declare const axios: AxiosStatic;

View File

@ -5,7 +5,7 @@ var bind = require('./helpers/bind');
var Axios = require('./core/Axios');
var mergeConfig = require('./core/mergeConfig');
var defaults = require('./defaults');
var formDataToJSON = require('./helpers/formDataToJSON');
/**
* Create an instance of Axios
*
@ -58,6 +58,10 @@ axios.spread = require('./helpers/spread');
// Expose isAxiosError
axios.isAxiosError = require('./helpers/isAxiosError');
axios.formToJSON = function(thing) {
return formDataToJSON(utils.isHTMLForm(thing) ? new FormData(thing) : thing);
};
module.exports = axios;
// Allow use of default import syntax in TypeScript

View File

@ -25,7 +25,8 @@ function Axios(instanceConfig) {
/**
* Dispatch a request
*
* @param {Object} config The config specific for this request (merged with this.defaults)
* @param {String|Object} configOrUrl The config specific for this request (merged with this.defaults)
* @param {?Object} config
*/
Axios.prototype.request = function request(configOrUrl, config) {
/*eslint no-param-reassign:0*/

View File

@ -7,6 +7,7 @@ var transitionalDefaults = require('./transitional');
var toFormData = require('../helpers/toFormData');
var toURLEncodedForm = require('../helpers/toURLEncodedForm');
var platform = require('../platform');
var formDataToJSON = require('../helpers/formDataToJSON');
var DEFAULT_CONTENT_TYPE = {
'Content-Type': 'application/x-www-form-urlencoded'
@ -55,8 +56,24 @@ var defaults = {
normalizeHeaderName(headers, 'Accept');
normalizeHeaderName(headers, 'Content-Type');
if (utils.isFormData(data) ||
utils.isArrayBuffer(data) ||
var contentType = headers && headers['Content-Type'] || '';
var hasJSONContentType = contentType.indexOf('application/json') > -1;
var isObjectPayload = utils.isObject(data);
if (isObjectPayload && utils.isHTMLForm(data)) {
data = new FormData(data);
}
var isFormData = utils.isFormData(data);
if (isFormData) {
if (!hasJSONContentType) {
return data;
}
return hasJSONContentType ? JSON.stringify(formDataToJSON(data)) : data;
}
if (utils.isArrayBuffer(data) ||
utils.isBuffer(data) ||
utils.isStream(data) ||
utils.isFile(data) ||
@ -72,8 +89,6 @@ var defaults = {
return data.toString();
}
var isObjectPayload = utils.isObject(data);
var contentType = headers && headers['Content-Type'] || '';
var isFileList;
if (isObjectPayload) {
@ -81,7 +96,7 @@ var defaults = {
return toURLEncodedForm(data, this.formSerializer).toString();
}
if ((isFileList = utils.isFileList(data)) || contentType.indexOf('multipart/form-data') !== -1) {
if ((isFileList = utils.isFileList(data)) || contentType.indexOf('multipart/form-data') > -1) {
var _FormData = this.env && this.env.FormData;
return toFormData(
@ -92,7 +107,7 @@ var defaults = {
}
}
if (isObjectPayload || contentType.indexOf('application/json') !== -1) {
if (isObjectPayload || hasJSONContentType ) {
setContentTypeIfUnset(headers, 'application/json');
return stringifySafely(data);
}

View File

@ -0,0 +1,71 @@
'use strict';
var utils = require('../utils');
function parsePropPath(name) {
// foo[x][y][z]
// foo.x.y.z
// foo-x-y-z
// foo x y z
return utils.matchAll(/\w+|\[(\w*)]/g, name).map(function(match) {
return match[0] === '[]' ? '' : match[1] || match[0];
});
}
function arrayToObject(arr) {
var obj = {};
var keys = Object.keys(arr);
var i;
var len = keys.length;
var key;
for (i = 0; i < len; i++) {
key = keys[i];
obj[key] = arr[key];
}
return obj;
}
function formDataToJSON(formData) {
function buildPath(path, value, target, index) {
var name = path[index++];
var isNumericKey = Number.isFinite(+name);
var isLast = index >= path.length;
name = !name && utils.isArray(target) ? target.length : name;
if (isLast) {
if (utils.hasOwnProperty(target, name)) {
target[name] = [target[name], value];
} else {
target[name] = value;
}
return !isNumericKey;
}
if (!target[name] || !utils.isObject(target[name])) {
target[name] = [];
}
var result = buildPath(path, value, target[name], index);
if (result && utils.isArray(target[name])) {
target[name] = arrayToObject(target[name]);
}
return !isNumericKey;
}
if (utils.isFormData(formData) && utils.isFunction(formData.entries)) {
var obj = {};
utils.forEachEntry(formData, function(name, value) {
buildPath(parsePropPath(name), value, obj, 0);
});
return obj;
}
return null;
}
module.exports = formDataToJSON;

View File

@ -441,6 +441,38 @@ var isTypedArray = (function(TypedArray) {
};
})(typeof Uint8Array !== 'undefined' && Object.getPrototypeOf(Uint8Array));
function forEachEntry(obj, fn) {
var generator = obj && obj[Symbol.iterator];
var iterator = generator.call(obj);
var result;
while ((result = iterator.next()) && !result.done) {
var pair = result.value;
fn.call(obj, pair[0], pair[1]);
}
}
function matchAll(regExp, str) {
var matches;
var arr = [];
while ((matches = regExp.exec(str)) !== null) {
arr.push(matches);
}
return arr;
}
var isHTMLForm = kindOfTest('HTMLFormElement');
var hasOwnProperty = (function resolver(_hasOwnProperty) {
return function(obj, prop) {
return _hasOwnProperty.call(obj, prop);
};
})(Object.prototype.hasOwnProperty);
module.exports = {
isArray: isArray,
isArrayBuffer: isArrayBuffer,
@ -471,5 +503,9 @@ module.exports = {
endsWith: endsWith,
toArray: toArray,
isTypedArray: isTypedArray,
isFileList: isFileList
isFileList: isFileList,
forEachEntry: forEachEntry,
matchAll: matchAll,
isHTMLForm: isHTMLForm,
hasOwnProperty: hasOwnProperty
};

View File

@ -0,0 +1,50 @@
var formDataToJSON = require('../../../lib/helpers/formDataToJSON');
describe('formDataToJSON', function () {
it('should convert a FormData Object to JSON Object', function () {
const formData = new FormData();
formData.append('foo[bar][baz]', '123');
expect(formDataToJSON(formData)).toEqual({
foo: {
bar: {
baz: '123'
}
}
});
});
it('should convert repeatable values as an array', function () {
const formData = new FormData();
formData.append('foo', '1');
formData.append('foo', '2');
expect(formDataToJSON(formData)).toEqual({
foo: ['1', '2']
});
});
it('should convert props with empty brackets to arrays', function () {
const formData = new FormData();
formData.append('foo[]', '1');
formData.append('foo[]', '2');
expect(formDataToJSON(formData)).toEqual({
foo: ['1', '2']
});
});
it('should supported indexed arrays', function () {
const formData = new FormData();
formData.append('foo[0]', '1');
formData.append('foo[1]', '2');
expect(formDataToJSON(formData)).toEqual({
foo: ['1', '2']
});
});
});

View File

@ -25,7 +25,8 @@ describe('instance', function () {
'isAxiosError',
'VERSION',
'default',
'toFormData'
'toFormData',
'formToJSON'
].indexOf(prop) > -1) {
continue;
}