Feat: Update serialization logic for application/x-www-form-urlencoded body type (#4943)

* fix: update qs.stringify to use repeat array format for url serialization

* fix(cli): update qs.stringify to use repeat array format for url serialization

* feat(tests): add URL serialization test case for Duplicate Keys

* feat(cli): refactor formUrlEncoded handling to use buildFormUrlEncodedPayload function

* fix(cli): standardize quotes in qs.stringify for form-urlencoded data

* fix(electron): standardize quotes in qs.stringify for form-urlencoded data
This commit is contained in:
lohit
2025-06-26 13:52:56 +05:30
committed by GitHub
6 changed files with 68 additions and 10 deletions

View File

@@ -2,7 +2,7 @@ const { get, each, filter } = require('lodash');
const decomment = require('decomment');
const crypto = require('node:crypto');
const { mergeHeaders, mergeScripts, mergeVars, mergeAuth, getTreePathFromCollectionToItem } = require('../utils/collection');
const { createFormData } = require('../utils/form-data');
const { buildFormUrlEncodedPayload } = require('../utils/form-data');
const prepareRequest = (item = {}, collection = {}) => {
const request = item?.request;
@@ -288,13 +288,13 @@ const prepareRequest = (item = {}, collection = {}) => {
}
if (request.body.mode === 'formUrlEncoded') {
axiosRequest.headers['content-type'] = 'application/x-www-form-urlencoded';
const params = {};
if (!contentTypeDefined) {
axiosRequest.headers['content-type'] = 'application/x-www-form-urlencoded';
}
const enabledParams = filter(request.body.formUrlEncoded, (p) => p.enabled);
each(enabledParams, (p) => (params[p.name] = p.value));
axiosRequest.data = params;
axiosRequest.data = buildFormUrlEncodedPayload(enabledParams);
}
if (request.body.mode === 'multipartForm') {
axiosRequest.headers['content-type'] = 'multipart/form-data';
const enabledParams = filter(request.body.multipartForm, (p) => p.enabled);

View File

@@ -329,11 +329,14 @@ const runSingleRequest = async function (
}
// stringify the request url encoded params
if (request.headers['content-type'] === 'application/x-www-form-urlencoded') {
request.data = qs.stringify(request.data);
const contentTypeHeader = Object.keys(request.headers).find(
name => name.toLowerCase() === 'content-type'
);
if (contentTypeHeader && request.headers[contentTypeHeader] === 'application/x-www-form-urlencoded') {
request.data = qs.stringify(request.data, { arrayFormat: 'repeat' });
}
if (request?.headers?.['content-type'] === 'multipart/form-data') {
if (contentTypeHeader && request.headers[contentTypeHeader] === 'multipart/form-data') {
if (!(request?.data instanceof FormData)) {
let form = createFormData(request.data, collectionPath);
request.data = form;

View File

@@ -3,6 +3,25 @@ const FormData = require('form-data');
const fs = require('fs');
const path = require('path');
/**
* @param {Array.<object>} params The request body Array
* @returns {object} Returns an obj with repeating key as an array of values
* {item: 2, item: 3, item1: 4} becomes {item: [2,3], item1: 4}
*/
const buildFormUrlEncodedPayload = (params) => {
return params.reduce((acc, p) => {
if (!acc[p.name]) {
acc[p.name] = p.value;
} else if (Array.isArray(acc[p.name])) {
acc[p.name].push(p.value);
} else {
acc[p.name] = [acc[p.name], p.value];
}
return acc;
}, {});
};
const createFormData = (data, collectionPath) => {
// make axios work in node using form data
// reference: https://github.com/axios/axios/issues/1006#issuecomment-320165427
@@ -38,5 +57,6 @@ const createFormData = (data, collectionPath) => {
};
module.exports = {
buildFormUrlEncodedPayload,
createFormData
}

View File

@@ -454,7 +454,7 @@ const registerNetworkIpc = (mainWindow) => {
// stringify the request url encoded params
if (request.headers['content-type'] === 'application/x-www-form-urlencoded') {
request.data = qs.stringify(request.data);
request.data = qs.stringify(request.data, { arrayFormat: 'repeat' });
}
if (request.headers['content-type'] === 'multipart/form-data') {

View File

@@ -0,0 +1,27 @@
meta {
name: Duplicate Keys
type: http
seq: 1
}
post {
url: https://echo.usebruno.com
body: formUrlEncoded
auth: none
}
headers {
Content-Type: application/x-www-form-urlencoded
}
body:form-urlencoded {
tags: frontend
tags: api
user: john
}
script:post-response {
test('Response body matches expected value', function () {
expect(res.getBody()).to.eql("tags=frontend&tags=api&user=john");
});
}

View File

@@ -0,0 +1,8 @@
meta {
name: url-serialization
seq: 13
}
auth {
mode: inherit
}