Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added axios.formToJSON method; #4735

Merged
merged 4 commits into from May 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions index.d.ts
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
6 changes: 5 additions & 1 deletion lib/axios.js
Expand Up @@ -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
*
Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion lib/core/Axios.js
Expand Up @@ -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*/
Expand Down
27 changes: 21 additions & 6 deletions lib/defaults/index.js
Expand Up @@ -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'
Expand Down Expand Up @@ -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) ||
Expand All @@ -72,16 +89,14 @@ var defaults = {
return data.toString();
}

var isObjectPayload = utils.isObject(data);
var contentType = headers && headers['Content-Type'] || '';
var isFileList;

if (isObjectPayload) {
if (contentType.indexOf('application/x-www-form-urlencoded') !== -1) {
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(
Expand All @@ -92,7 +107,7 @@ var defaults = {
}
}

if (isObjectPayload || contentType.indexOf('application/json') !== -1) {
if (isObjectPayload || hasJSONContentType ) {
setContentTypeIfUnset(headers, 'application/json');
return stringifySafely(data);
}
Expand Down
71 changes: 71 additions & 0 deletions lib/helpers/formDataToJSON.js
@@ -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;
38 changes: 37 additions & 1 deletion lib/utils.js
Expand Up @@ -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,
Expand Down Expand Up @@ -471,5 +503,9 @@ module.exports = {
endsWith: endsWith,
toArray: toArray,
isTypedArray: isTypedArray,
isFileList: isFileList
isFileList: isFileList,
forEachEntry: forEachEntry,
matchAll: matchAll,
isHTMLForm: isHTMLForm,
hasOwnProperty: hasOwnProperty
};
50 changes: 50 additions & 0 deletions test/specs/helpers/formDataToJSON.spec.js
@@ -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']
});
});
});
3 changes: 2 additions & 1 deletion test/specs/instance.spec.js
Expand Up @@ -25,7 +25,8 @@ describe('instance', function () {
'isAxiosError',
'VERSION',
'default',
'toFormData'
'toFormData',
'formToJSON'
].indexOf(prop) > -1) {
continue;
}
Expand Down