diff --git a/index.d.ts b/index.d.ts index b681915e16..5cb94c5751 100644 --- a/index.d.ts +++ b/index.d.ts @@ -107,6 +107,9 @@ export interface AxiosRequestConfig { transitional?: TransitionalOptions; signal?: AbortSignal; insecureHTTPParser?: boolean; + env?: { + FormData?: new (...args: any[]) => object; + }; } export interface HeadersDefaults { diff --git a/lib/adapters/http.js b/lib/adapters/http.js index 951e400fa6..5753fbdddd 100755 --- a/lib/adapters/http.js +++ b/lib/adapters/http.js @@ -87,7 +87,9 @@ module.exports = function httpAdapter(config) { headers['User-Agent'] = 'axios/' + VERSION; } - if (data && !utils.isStream(data)) { + if (utils.isFormData(data) && utils.isFunction(data.getHeaders)) { + Object.assign(headers, data.getHeaders()); + } else if (data && !utils.isStream(data)) { if (Buffer.isBuffer(data)) { // Nothing to do... } else if (utils.isArrayBuffer(data)) { diff --git a/lib/defaults.js b/lib/defaults.js index eaee1898a0..cb061af4c0 100644 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -3,6 +3,7 @@ var utils = require('./utils'); var normalizeHeaderName = require('./helpers/normalizeHeaderName'); var enhanceError = require('./core/enhanceError'); +var toFormData = require('./helpers/toFormData'); var DEFAULT_CONTENT_TYPE = { 'Content-Type': 'application/x-www-form-urlencoded' @@ -71,10 +72,17 @@ var defaults = { setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8'); return data.toString(); } - if (utils.isObject(data) || (headers && headers['Content-Type'] === 'application/json')) { + + var isObjectPayload = utils.isObject(data); + var contentType = headers && headers['Content-Type']; + + if ( isObjectPayload && contentType === 'multipart/form-data' ) { + return toFormData(data, new (this.env && this.env.FormData || FormData)); + } else if ( isObjectPayload || contentType === 'application/json' ) { setContentTypeIfUnset(headers, 'application/json'); return stringifySafely(data); } + return data; }], @@ -112,6 +120,8 @@ var defaults = { maxContentLength: -1, maxBodyLength: -1, + env: {}, + validateStatus: function validateStatus(status) { return status >= 200 && status < 300; }, diff --git a/lib/helpers/toFormData.js b/lib/helpers/toFormData.js index e21d0a7fa6..0f03b79861 100644 --- a/lib/helpers/toFormData.js +++ b/lib/helpers/toFormData.js @@ -1,5 +1,7 @@ 'use strict'; +var utils = require('../utils'); + function combinedKey(parentKey, elKey) { return parentKey + '.' + elKey; } @@ -11,7 +13,7 @@ function buildFormData(formData, data, parentKey) { }); } else if ( typeof data === 'object' && - !(data instanceof File || data === null) + !(utils.isFile(data) || data === null) ) { Object.keys(data).forEach(function buildObject(key) { buildFormData( @@ -44,10 +46,12 @@ function buildFormData(formData, data, parentKey) { * type FormVal = FormDataNest | FormDataPrimitive * * @param {FormVal} data + * @param {?Object} formData */ -module.exports = function getFormData(data) { - var formData = new FormData(); +module.exports = function getFormData(data, formData) { + // eslint-disable-next-line no-param-reassign + formData = formData || new FormData(); buildFormData(formData, data); diff --git a/lib/utils.js b/lib/utils.js index f0f90432c5..536fbf9336 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -47,15 +47,6 @@ function isArrayBuffer(val) { return toString.call(val) === '[object ArrayBuffer]'; } -/** - * Determine if a value is a FormData - * - * @param {Object} val The value to test - * @returns {boolean} True if value is an FormData, otherwise false - */ -function isFormData(val) { - return toString.call(val) === '[object FormData]'; -} /** * Determine if a value is a view on an ArrayBuffer @@ -168,6 +159,21 @@ function isStream(val) { return isObject(val) && isFunction(val.pipe); } +/** + * Determine if a value is a FormData + * + * @param {Object} thing The value to test + * @returns {boolean} True if value is an FormData, otherwise false + */ +function isFormData(thing) { + var pattern = '[object FormData]'; + return thing && ( + (typeof FormData === 'function' && thing instanceof FormData) || + toString.call(thing) === pattern || + (isFunction(thing.toString) && thing.toString() === pattern) + ); +} + /** * Determine if a value is a URLSearchParams object * diff --git a/test/specs/helpers/toFormData.spec.js b/test/specs/helpers/toFormData.spec.js index f28471ece3..ec16252cd3 100644 --- a/test/specs/helpers/toFormData.spec.js +++ b/test/specs/helpers/toFormData.spec.js @@ -1,7 +1,7 @@ var toFormData = require("../../../lib/helpers/toFormData"); describe("toFormData", function () { - it("Convert nested data object to FormDAta", function () { + it("Convert nested data object to FormData", function () { var o = { val: 123, nested: {