From b0ebf9fcacbab3e5a020a1a4d5d3837342d7dcd7 Mon Sep 17 00:00:00 2001 From: ChronosMasterOfAllTime Date: Sat, 15 Oct 2022 08:01:59 -0500 Subject: [PATCH] Fix: Don't add null values to query string. (#5108) * feat: add boolean flag to mimic pre 1.x behavior for paramsSerializer custom function * chore: update ParamsSerializer Readme * fix: dont slice hash off URL if not appending params * Omit nulls from formData serialization * fix: dont add nulls or undefined values to arrays either * readme update * fix test * chore: documentation * chore: do TS properly Co-authored-by: Jay --- README.md | 2 ++ index.d.ts | 2 +- lib/helpers/buildURL.js | 13 ++++++------- lib/helpers/toFormData.js | 4 ++-- test/specs/helpers/buildURL.spec.js | 25 +++++++++++++++++++++++-- test/typescript/axios.ts | 6 +++--- 6 files changed, 37 insertions(+), 15 deletions(-) mode change 100755 => 100644 README.md diff --git a/README.md b/README.md old mode 100755 new mode 100644 index 9ffde41ee7..b7260413fc --- a/README.md +++ b/README.md @@ -358,6 +358,8 @@ These are the available config options for making requests. Only the `url` is re // `paramsSerializer` is an optional config in charge of serializing `params` paramsSerializer: { + encode?: (param: string): string => { /* Do custom ops here and return transformed string */ }, // custom encoder function; sends Key/Values in an iterative fashion + serialize?: (params: Record, options?: ParamsSerializerOptions ), // mimic pre 1.x behavior and send entire params object to a custom serializer func. Allows consumer to control how params are serialized. indexes: false // array indexes format (null - no brackets, false (default) - empty brackets, true - brackets with indexes) }, diff --git a/index.d.ts b/index.d.ts index d125d0bb80..abc4d93aee 100644 --- a/index.d.ts +++ b/index.d.ts @@ -248,7 +248,7 @@ export interface ParamEncoder { } export interface CustomParamsSerializer { - (params: object, options?: ParamsSerializerOptions): string; + (params: Record, options?: ParamsSerializerOptions): string; } export interface ParamsSerializerOptions extends SerializerOptions { diff --git a/lib/helpers/buildURL.js b/lib/helpers/buildURL.js index 5256194170..d769fdf4de 100644 --- a/lib/helpers/buildURL.js +++ b/lib/helpers/buildURL.js @@ -35,13 +35,7 @@ export default function buildURL(url, params, options) { if (!params) { return url; } - - const hashmarkIndex = url.indexOf('#'); - - if (hashmarkIndex !== -1) { - url = url.slice(0, hashmarkIndex); - } - + const _encode = options && options.encode || encode; const serializeFn = options && options.serialize; @@ -57,6 +51,11 @@ export default function buildURL(url, params, options) { } if (serializedParams) { + const hashmarkIndex = url.indexOf("#"); + + if (hashmarkIndex !== -1) { + url = url.slice(0, hashmarkIndex); + } url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams; } diff --git a/lib/helpers/toFormData.js b/lib/helpers/toFormData.js index 1d89e7fc6e..ce99ce15c7 100644 --- a/lib/helpers/toFormData.js +++ b/lib/helpers/toFormData.js @@ -168,7 +168,7 @@ function toFormData(obj, formData, options) { key = removeBrackets(key); arr.forEach(function each(el, index) { - !utils.isUndefined(el) && formData.append( + !(utils.isUndefined(el) || el === null) && formData.append( // eslint-disable-next-line no-nested-ternary indexes === true ? renderKey([key], index, dots) : (indexes === null ? key : key + '[]'), convertValue(el) @@ -205,7 +205,7 @@ function toFormData(obj, formData, options) { stack.push(value); utils.forEach(value, function each(el, key) { - const result = !utils.isUndefined(el) && visitor.call( + const result = !(utils.isUndefined(el) || el === null) && visitor.call( formData, el, utils.isString(key) ? key.trim() : key, path, exposedHelpers ); diff --git a/test/specs/helpers/buildURL.spec.js b/test/specs/helpers/buildURL.spec.js index b6ea22d0b7..ce525a34b5 100644 --- a/test/specs/helpers/buildURL.spec.js +++ b/test/specs/helpers/buildURL.spec.js @@ -8,10 +8,31 @@ describe('helpers::buildURL', function () { it('should support params', function () { expect(buildURL('/foo', { - foo: 'bar' + foo: 'bar', + isUndefined: undefined, + isNull: null })).toEqual('/foo?foo=bar'); }); + it('should support sending raw params to custom serializer func', function () { + const serializer = sinon.stub(); + const params = { foo: "bar" }; + serializer.returns("foo=bar"); + expect( + buildURL( + "/foo", + { + foo: "bar", + }, + { + serialize: serializer, + } + ) + ).toEqual("/foo?foo=bar"); + expect(serializer.calledOnce).toBe(true); + expect(serializer.calledWith(params)).toBe(true); + }); + it('should support object params', function () { expect(buildURL('/foo', { foo: { @@ -30,7 +51,7 @@ describe('helpers::buildURL', function () { it('should support array params', function () { expect(buildURL('/foo', { - foo: ['bar', 'baz'] + foo: ['bar', 'baz', null, undefined] })).toEqual('/foo?foo[]=bar&foo[]=baz'); }); diff --git a/test/typescript/axios.ts b/test/typescript/axios.ts index 1ba5a955e2..617993eb43 100644 --- a/test/typescript/axios.ts +++ b/test/typescript/axios.ts @@ -6,7 +6,7 @@ import axios, { AxiosAdapter, Cancel, CancelTokenSource, - Canceler, AxiosProgressEvent + Canceler, AxiosProgressEvent, ParamsSerializerOptions } from 'axios'; const config: AxiosRequestConfig = { @@ -21,8 +21,8 @@ const config: AxiosRequestConfig = { params: { id: 12345 }, paramsSerializer: { indexes: true, - encode: (value) => value, - serialize: (value, options) => String(value) + encode: (value: any) => value, + serialize: (value: Record, options?: ParamsSerializerOptions) => String(value) }, data: { foo: 'bar' }, timeout: 10000,