Skip to content

Commit 24704ea

Browse files
committed
fix: put requests with application/x-www-form-urlencoded is incorrectly handled
1 parent 2f0aa68 commit 24704ea

File tree

25 files changed

+1494
-13
lines changed

25 files changed

+1494
-13
lines changed

.changeset/fix-form-urlencoded-put.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"swagger-typescript-api": patch
3+
---
4+
5+
Fix: PUT requests with application/x-www-form-urlencoded content type
6+
7+
This fixes an issue where PUT requests with `application/x-www-form-urlencoded` content type were incorrectly sent as `multipart/form-data`.
8+
A new `createUrlEncoded` method has been added to the `HttpClient` to handle this content type correctly.

src/schema-routes/schema-routes.ts

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -561,21 +561,48 @@ export class SchemaRoutes {
561561
});
562562
}
563563

564-
if (routeParams.formData.length) {
565-
contentKind = CONTENT_KIND.FORM_DATA;
564+
if (
565+
contentKind === CONTENT_KIND.URL_ENCODED &&
566+
routeParams.formData.length
567+
) {
566568
schema = this.convertRouteParamsIntoObject(routeParams.formData);
567569
content = this.schemaParserFabric.getInlineParseContent(
568570
schema,
569571
typeName,
570572
[operationId],
571573
);
572-
} else if (contentKind === CONTENT_KIND.FORM_DATA) {
573-
schema = this.getSchemaFromRequestType(requestBody);
574+
} else if (routeParams.formData.length) {
575+
contentKind = CONTENT_KIND.FORM_DATA;
576+
schema = this.convertRouteParamsIntoObject(routeParams.formData);
574577
content = this.schemaParserFabric.getInlineParseContent(
575578
schema,
576579
typeName,
577580
[operationId],
578581
);
582+
} else if (contentKind === CONTENT_KIND.URL_ENCODED) {
583+
schema = this.getSchemaFromRequestType(requestBody);
584+
content = this.schemaParserFabric.schemaUtils.safeAddNullToType(
585+
requestBody,
586+
this.getTypeFromRequestInfo({
587+
requestInfo: requestBody,
588+
parsedSchemas,
589+
operationId,
590+
defaultType: "any",
591+
typeName,
592+
}),
593+
);
594+
} else if (contentKind === CONTENT_KIND.FORM_DATA) {
595+
schema = this.getSchemaFromRequestType(requestBody);
596+
content = this.schemaParserFabric.schemaUtils.safeAddNullToType(
597+
requestBody,
598+
this.getTypeFromRequestInfo({
599+
requestInfo: requestBody,
600+
parsedSchemas,
601+
operationId,
602+
defaultType: "any",
603+
typeName,
604+
}),
605+
);
579606
} else if (requestBody) {
580607
schema = this.getSchemaFromRequestType(requestBody);
581608
content = this.schemaParserFabric.schemaUtils.safeAddNullToType(
@@ -1074,6 +1101,12 @@ export class SchemaRoutes {
10741101
security: hasSecurity,
10751102
method: method,
10761103
requestParams: requestParamsSchema,
1104+
type:
1105+
requestBodyInfo.contentKind === CONTENT_KIND.FORM_DATA
1106+
? "multipart/form-data"
1107+
: requestBodyInfo.contentKind === CONTENT_KIND.URL_ENCODED
1108+
? "application/x-www-form-urlencoded"
1109+
: undefined,
10771110

10781111
payload: specificArgs.body,
10791112
query: specificArgs.query,

templates/base/http-clients/axios-http-client.ejs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,22 @@ export class HttpClient<SecurityDataType = unknown> {
9999
}, new FormData());
100100
}
101101

102+
protected createUrlEncoded(input: Record<string, unknown>): URLSearchParams {
103+
if (input instanceof URLSearchParams) {
104+
return input;
105+
}
106+
return Object.keys(input || {}).reduce((searchParams, key) => {
107+
const property = input[key];
108+
const propertyContent: any[] = (property instanceof Array) ? property : [property]
109+
110+
for (const formItem of propertyContent) {
111+
searchParams.append(key, this.stringifyFormItem(formItem));
112+
}
113+
114+
return searchParams;
115+
}, new URLSearchParams());
116+
}
117+
102118
public request = async <T = any, _E = any>({
103119
secure,
104120
path,
@@ -120,6 +136,10 @@ export class HttpClient<SecurityDataType = unknown> {
120136
body = this.createFormData(body as Record<string, unknown>);
121137
}
122138

139+
if (type === ContentType.UrlEncoded && body && body !== null && typeof body === "object") {
140+
body = this.createUrlEncoded(body as Record<string, unknown>);
141+
}
142+
123143
if (type === ContentType.Text && body && body !== null && typeof body !== "string") {
124144
body = JSON.stringify(body);
125145
}

tests/__snapshots__/extended.test.ts.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6376,7 +6376,7 @@ export class Api<
63766376
method: "POST",
63776377
body: data,
63786378
secure: true,
6379-
type: ContentType.FormData,
6379+
type: ContentType.UrlEncoded,
63806380
...params,
63816381
}),
63826382

@@ -69452,7 +69452,7 @@ export class Api<
6945269452
method: "POST",
6945369453
body: data,
6945469454
secure: true,
69455-
type: ContentType.FormData,
69455+
type: ContentType.UrlEncoded,
6945669456
...params,
6945769457
}),
6945869458

tests/__snapshots__/simple.test.ts.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3968,7 +3968,7 @@ export class Api<
39683968
method: "POST",
39693969
body: data,
39703970
secure: true,
3971-
type: ContentType.FormData,
3971+
type: ContentType.UrlEncoded,
39723972
...params,
39733973
}),
39743974

@@ -43623,7 +43623,7 @@ export class Api<
4362343623
method: "POST",
4362443624
body: data,
4362543625
secure: true,
43626-
type: ContentType.FormData,
43626+
type: ContentType.UrlEncoded,
4362743627
...params,
4362843628
}),
4362943629

tests/spec/axios/__snapshots__/basic.test.ts.snap

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2037,6 +2037,23 @@ export class HttpClient<SecurityDataType = unknown> {
20372037
}, new FormData());
20382038
}
20392039

2040+
protected createUrlEncoded(input: Record<string, unknown>): URLSearchParams {
2041+
if (input instanceof URLSearchParams) {
2042+
return input;
2043+
}
2044+
return Object.keys(input || {}).reduce((searchParams, key) => {
2045+
const property = input[key];
2046+
const propertyContent: any[] =
2047+
property instanceof Array ? property : [property];
2048+
2049+
for (const formItem of propertyContent) {
2050+
searchParams.append(key, this.stringifyFormItem(formItem));
2051+
}
2052+
2053+
return searchParams;
2054+
}, new URLSearchParams());
2055+
}
2056+
20402057
public request = async <T = any, _E = any>({
20412058
secure,
20422059
path,
@@ -2063,6 +2080,15 @@ export class HttpClient<SecurityDataType = unknown> {
20632080
body = this.createFormData(body as Record<string, unknown>);
20642081
}
20652082

2083+
if (
2084+
type === ContentType.UrlEncoded &&
2085+
body &&
2086+
body !== null &&
2087+
typeof body === "object"
2088+
) {
2089+
body = this.createUrlEncoded(body as Record<string, unknown>);
2090+
}
2091+
20662092
if (
20672093
type === ContentType.Text &&
20682094
body &&

tests/spec/axiosSingleHttpClient/__snapshots__/basic.test.ts.snap

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2037,6 +2037,23 @@ export class HttpClient<SecurityDataType = unknown> {
20372037
}, new FormData());
20382038
}
20392039

2040+
protected createUrlEncoded(input: Record<string, unknown>): URLSearchParams {
2041+
if (input instanceof URLSearchParams) {
2042+
return input;
2043+
}
2044+
return Object.keys(input || {}).reduce((searchParams, key) => {
2045+
const property = input[key];
2046+
const propertyContent: any[] =
2047+
property instanceof Array ? property : [property];
2048+
2049+
for (const formItem of propertyContent) {
2050+
searchParams.append(key, this.stringifyFormItem(formItem));
2051+
}
2052+
2053+
return searchParams;
2054+
}, new URLSearchParams());
2055+
}
2056+
20402057
public request = async <T = any, _E = any>({
20412058
secure,
20422059
path,
@@ -2063,6 +2080,15 @@ export class HttpClient<SecurityDataType = unknown> {
20632080
body = this.createFormData(body as Record<string, unknown>);
20642081
}
20652082

2083+
if (
2084+
type === ContentType.UrlEncoded &&
2085+
body &&
2086+
body !== null &&
2087+
typeof body === "object"
2088+
) {
2089+
body = this.createUrlEncoded(body as Record<string, unknown>);
2090+
}
2091+
20662092
if (
20672093
type === ContentType.Text &&
20682094
body &&

0 commit comments

Comments
 (0)