Skip to content

[csharp][generichost] Better file support#22806

Open
devhl-labs wants to merge 15 commits intoOpenAPITools:masterfrom
devhl-labs:devhl/21380-multi-file-uploads-2
Open

[csharp][generichost] Better file support#22806
devhl-labs wants to merge 15 commits intoOpenAPITools:masterfrom
devhl-labs:devhl/21380-multi-file-uploads-2

Conversation

@devhl-labs
Copy link
Contributor

@devhl-labs devhl-labs commented Jan 25, 2026

[C# generichost] Add multi-file upload support and introduce FileParameter

Fixes #21380
Fixes #21384

Summary

This PR improves file upload handling in the C# generichost generator by introducing a dedicated FileParameter class and fixing the generated multipart form-data request code. It also adds support for uploading multiple files in a single request (array of binary form parameters).

Changes

New FileParameter class (FileParameter.mustache)

A new supporting file is generated into the Client package. It wraps a Stream and an optional filename, giving callers a clean way to supply both the file content and the Content-Disposition filename without relying on a raw Stream.

public sealed class FileParameter
{
    public Stream Content { get; }
    public string? FileName { get; }

    public FileParameter(Stream content, string? fileName = null) { ... }
}

New OperationDataType.mustache partial

A new partial template resolves the C# type for a parameter, substituting FileParameter (or List<FileParameter>) wherever the OpenAPI spec declares a binary file parameter. All other parameters continue to resolve through the existing dataType path.

Updated OperationSignature.mustache

The operation signature template now delegates to OperationDataType so that file parameters in method signatures are typed as FileParameter instead of System.IO.Stream.

Updated api.mustache

  • MultipartContentMultipartFormDataContent: The generated multipart request now uses MultipartFormDataContent, which sets the correct Content-Type: multipart/form-data; boundary=... header automatically.
  • Single file upload: Reads FileParameter.Content as a StreamContent and adds it with the part name and FileParameter.FileName (falling back to the spec parameter name).
  • Multi-file upload (isContainer): Iterates over List<FileParameter> and adds each entry as a separate multipart part with its own name and filename.
  • Form-url-encoded + file = error: When a file parameter is combined with application/x-www-form-urlencoded, a NotSupportedException is thrown at runtime with a clear message directing the caller to use multipart/form-data instead.
  • Text form params with multipart: Non-file form fields are now added to the multipart content as StringContent parts (previously they were lost).
  • Content-type selection skipped for form params: The automatic ClientUtils.SelectHeaderContentType call is suppressed when form parameters are present, since MultipartFormDataContent and FormUrlEncodedContent manage their own Content-Type headers.
  • Format / Validate / After / OnError partial signatures: Updated to use OperationDataType so the partial method signatures stay consistent with the public API.

Updated api_test.mustache

Generated test stubs now use OperationDataType for parameter declarations, so test files compile correctly when file parameters are present.

Updated CSharpClientCodegen.java

FileParameter.mustache is registered as a supporting file so it is emitted for every generated client.

New test endpoint in the petstore YAML

Added POST /pet/{petId}/uploadImages with a required files property typed as array of binary. This exercises the new multi-file upload path in the generated code.

Breaking change

File parameters in generated API interfaces and implementations change from System.IO.Stream / Option<System.IO.Stream> to FileParameter / Option<FileParameter> (and List<FileParameter> for array parameters). Callers must wrap their streams:

// Before
await petApi.UploadFileAsync(petId, file: myStream);

// After
await petApi.UploadFileAsync(petId, file: new FileParameter(myStream, "photo.jpg"));

Samples regenerated

All csharp/generichost samples have been regenerated to reflect the new FileParameter class and updated API signatures.

@wing328
Copy link
Member

wing328 commented Feb 5, 2026

@devhl-labs we will cut a release next week. Let me know if we should include this enhancement as well.

@devhl-labs
Copy link
Contributor Author

I dont expect it to be ready in time.

@wing328
Copy link
Member

wing328 commented Feb 5, 2026

👌 please take your time

@devhl-labs devhl-labs marked this pull request as ready for review March 8, 2026 22:50
@devhl-labs
Copy link
Contributor Author

@wing328 Ready

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

40 issues found across 137 files

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/client/petstore/csharp/generichost/net10/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net10/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs:4332">
P1: Form fields are serialized into a single unnamed `FormUrlEncodedContent` part inside `MultipartFormDataContent`, which can prevent normal multipart form-field binding.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net8/SourceGeneration/src/Org.OpenAPITools/Api/PetApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net8/SourceGeneration/src/Org.OpenAPITools/Api/PetApi.cs:2392">
P2: `UpdatePetWithFormAsync` now sends `multipart/form-data` despite operation content type being `application/x-www-form-urlencoded`, creating a request media-type mismatch.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net9/FormModels/src/Org.OpenAPITools/Api/PetApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net9/FormModels/src/Org.OpenAPITools/Api/PetApi.cs:2403">
P2: Form fields are bundled into a single unnamed multipart part (FormUrlEncodedContent), so multipart/form-data requests may not bind name/status/additionalMetadata. Each field should be its own part with a name.</violation>

<violation number="2" location="samples/client/petstore/csharp/generichost/net9/FormModels/src/Org.OpenAPITools/Api/PetApi.cs:3216">
P2: Multipart stream parts are sent without a `filename` in Content-Disposition, which can break file binding on servers expecting file dispositions.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/csharp/libraries/generichost/api.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/csharp/libraries/generichost/api.mustache:498">
P2: Multipart file parts are added without a filename, which can prevent ASP.NET Core IFormFile binding (sections without filename are treated as normal form values).</violation>

<violation number="2" location="modules/openapi-generator/src/main/resources/csharp/libraries/generichost/api.mustache:503">
P2: Required non-container file parameters reuse the same local variable name inside the formParams loop, so operations with multiple required file params will generate duplicate `streamContentLocalVar` declarations and fail to compile.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.8/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net4.8/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs:4275">
P2: Form-only operation now sends multipart/form-data while the OpenAPI contract declares application/x-www-form-urlencoded, causing a content-type mismatch for TestJsonFormDataAsync.</violation>

<violation number="2" location="samples/client/petstore/csharp/generichost/net4.8/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs:4292">
P2: Multipart file part is added without a filename, which can break server-side file binding (e.g., IFormFile).</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net8/FormModels/src/Org.OpenAPITools/Api/DefaultApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net8/FormModels/src/Org.OpenAPITools/Api/DefaultApi.cs:767">
P2: The /country endpoint declares application/x-www-form-urlencoded, but the generated client now always wraps the form body in MultipartFormDataContent, sending multipart/form-data and adding the urlencoded content as an unnamed part. This mismatches the OpenAPI contract and can break server-side form binding for non-file endpoints.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.8/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net4.8/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs:4323">
P2: Form fields are added as a single unnamed multipart part (`FormUrlEncodedContent` via `Add(HttpContent)`), which yields no `Content-Disposition: form-data; name=...` and can cause servers to ignore/break form-field binding in multipart/form-data requests.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.8/FormModels/src/Org.OpenAPITools/Api/DefaultApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net4.8/FormModels/src/Org.OpenAPITools/Api/DefaultApi.cs:766">
P2: GetCountry now always sends multipart/form-data with a nested FormUrlEncodedContent even though the operation declares only application/x-www-form-urlencoded, which can break servers expecting a top-level urlencoded body.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net8/NullReferenceTypes/src/Org.OpenAPITools/Api/DefaultApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net8/NullReferenceTypes/src/Org.OpenAPITools/Api/DefaultApi.cs:769">
P2: GetCountryAsync now always uses MultipartFormDataContent (multipart/form-data) even though the operation’s only declared request content type is application/x-www-form-urlencoded, and the content-type selection was removed. This mismatches the OpenAPI contract and can cause 415/failed model binding on servers expecting URL-encoded bodies.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net9/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net9/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs:4284">
P2: Client now sends multipart/form-data bodies for form operations, but the OpenAPI contract for this target still declares only application/x-www-form-urlencoded, so requests no longer match the documented consumes type.</violation>

<violation number="2" location="samples/client/petstore/csharp/generichost/net9/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs:4332">
P2: Form fields are added as a single unnamed FormUrlEncodedContent part inside multipart/form-data, so individual fields may not bind on the server. Each field should be added as its own multipart part with a name (Content-Disposition) instead of one unnamed URL-encoded part.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.8/FormModels/src/Org.OpenAPITools/Api/PetApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net4.8/FormModels/src/Org.OpenAPITools/Api/PetApi.cs:2399">
P2: Multipart text fields are added as a single unnamed `FormUrlEncodedContent` part. Because `Add(HttpContent)` doesn’t set a form-data `name` and `FormUrlEncodedContent` doesn’t include one, the multipart part is unnamed and typical server-side form binding won’t map `additionalMetadata`/`name`/`status` fields.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.7/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net4.7/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs:4292">
P2: Multipart file part is added without a filename, which can cause ASP.NET Core to treat it as a normal form field instead of a file, resulting in null/empty IFormFile binding.</violation>

<violation number="2" location="samples/client/petstore/csharp/generichost/net4.7/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs:4323">
P2: Multipart form fields are bundled into a single unnamed FormUrlEncodedContent part inside MultipartFormDataContent, which prevents standard multipart/form-data field binding on many servers.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net10/FormModels/src/Org.OpenAPITools/Api/DefaultApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net10/FormModels/src/Org.OpenAPITools/Api/DefaultApi.cs:767">
P2: GetCountryAsync now sends multipart/form-data while the operation still declares application/x-www-form-urlencoded, so urlencoded-only servers may reject or fail to bind the form body.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net8/SourceGeneration/src/Org.OpenAPITools/Api/DefaultApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net8/SourceGeneration/src/Org.OpenAPITools/Api/DefaultApi.cs:769">
P2: Request body is now built with MultipartFormDataContent even though contentTypes only includes application/x-www-form-urlencoded, so the actual Content-Type becomes multipart/form-data and can violate the API’s contract for urlencoded-only endpoints.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net10/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net10/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs:4332">
P2: Multipart form fields are still bundled into one FormUrlEncodedContent part, so multipart/form-data loses individual field names and standard server form binding can’t map them.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net10/SourceGeneration/src/Org.OpenAPITools/Api/PetApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net10/SourceGeneration/src/Org.OpenAPITools/Api/PetApi.cs:2392">
P2: UpdatePetWithFormAsync now sends multipart/form-data (MultipartFormDataContent) despite the operation declaring application/x-www-form-urlencoded, which can cause servers expecting urlencoded form data to reject or mis-bind the request.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net8/NullReferenceTypes/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net8/NullReferenceTypes/src/Org.OpenAPITools/Api/FakeApi.cs:4334">
P2: Multipart form fields are still being added as one unnamed FormUrlEncodedContent part. In multipart/form-data each field should be its own named part (Add(content, name)); an unnamed nested urlencoded part may be ignored by model binders, causing missing form fields when files are present.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net8/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net8/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs:4332">
P2: Multipart form fields are added as a single unnamed FormUrlEncodedContent part, which is not standard multipart/form-data and can prevent server-side form binding from seeing individual fields.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/standard2.0/Petstore/src/Org.OpenAPITools/Api/DefaultApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/standard2.0/Petstore/src/Org.OpenAPITools/Api/DefaultApi.cs:765">
P2: Request body is now always multipart/form-data even though the operation declares only application/x-www-form-urlencoded; removing the content-type selection means the client can send a media type the server doesn’t accept.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.7/Petstore/src/Org.OpenAPITools/Api/DefaultApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net4.7/Petstore/src/Org.OpenAPITools/Api/DefaultApi.cs:766">
P2: GetCountryAsync now sends multipart/form-data even though the operation declares application/x-www-form-urlencoded, which can break servers expecting urlencoded form bodies.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net9/Petstore/src/Org.OpenAPITools/Api/DefaultApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net9/Petstore/src/Org.OpenAPITools/Api/DefaultApi.cs:767">
P2: Request body is now always multipart/form-data even though the operation’s contentTypes list only includes application/x-www-form-urlencoded, causing a media-type mismatch for servers that accept only urlencoded form data.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net9/NullReferenceTypes/src/Org.OpenAPITools/Api/PetApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net9/NullReferenceTypes/src/Org.OpenAPITools/Api/PetApi.cs:2405">
P2: Form fields are still added as a single unnamed FormUrlEncodedContent part. Multipart/form-data expects each field to be its own part with a Content-Disposition name; without it, servers typically won’t bind fields like additionalMetadata/name/status.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net9/NullReferenceTypes/src/Org.OpenAPITools/Api/DefaultApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net9/NullReferenceTypes/src/Org.OpenAPITools/Api/DefaultApi.cs:769">
P2: GetCountryAsync now always sends multipart/form-data even though the operation declares only application/x-www-form-urlencoded, creating a request Content-Type mismatch that can break servers expecting urlencoded bodies.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.7/FormModels/src/Org.OpenAPITools/Api/DefaultApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net4.7/FormModels/src/Org.OpenAPITools/Api/DefaultApi.cs:766">
P2: GetCountryAsync now sends multipart/form-data even though the /country requestBody only allows application/x-www-form-urlencoded, which can cause media-type contract mismatches.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.8/Petstore/src/Org.OpenAPITools/Api/DefaultApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net4.8/Petstore/src/Org.OpenAPITools/Api/DefaultApi.cs:766">
P2: The /country request body is specified as application/x-www-form-urlencoded, but the updated client now wraps parameters in MultipartFormDataContent without setting the form-url-encoded Content-Type. This causes requests to be sent as multipart/form-data, which can break servers expecting the documented form-url-encoded payload.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.7/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net4.7/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs:4292">
P2: Multipart file part is added without a filename; ASP.NET Core file binders typically require a filename in Content-Disposition to treat it as a file upload, so the `binary` parameter may not bind.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net10/NullReferenceTypes/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net10/NullReferenceTypes/src/Org.OpenAPITools/Api/FakeApi.cs:4303">
P2: Multipart file part is added without a filename in Content-Disposition, which can cause ASP.NET Core/IFormFile binding to treat the part as a regular form field instead of a file. This can lead to file parameters binding as null even though bytes are sent.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net9/SourceGeneration/src/Org.OpenAPITools/Api/DefaultApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net9/SourceGeneration/src/Org.OpenAPITools/Api/DefaultApi.cs:769">
P2: Request body for /country is defined as application/x-www-form-urlencoded in the OpenAPI spec, but the client now always uses MultipartFormDataContent without setting the content type. This sends multipart/form-data and can cause 415/model binding failures on servers expecting urlencoded form bodies.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net9/SourceGeneration/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net9/SourceGeneration/src/Org.OpenAPITools/Api/FakeApi.cs:4303">
P2: Multipart file part is added without a filename, which can cause ASP.NET Core and similar servers to treat it as a normal form field instead of a file, breaking file binding.</violation>

<violation number="2" location="samples/client/petstore/csharp/generichost/net9/SourceGeneration/src/Org.OpenAPITools/Api/FakeApi.cs:4334">
P2: Multipart form fields are bundled into a single unnamed application/x-www-form-urlencoded part, so servers expecting individual multipart form-data fields (name=...) may not bind parameters.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net10/NullReferenceTypes/src/Org.OpenAPITools/Api/PetApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net10/NullReferenceTypes/src/Org.OpenAPITools/Api/PetApi.cs:2392">
P2: UpdatePetWithFormAsync now sends multipart/form-data even though the operation’s contentTypes only include application/x-www-form-urlencoded; this mismatch can cause the server to reject or ignore the form data.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net9/Petstore/src/Org.OpenAPITools/Api/PetApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net9/Petstore/src/Org.OpenAPITools/Api/PetApi.cs:2649">
P2: Multipart file parts are added without a filename, so Content-Disposition lacks a filename and ASP.NET Core/IFormFile binders may not treat the parts as files. Use the MultipartFormDataContent.Add(content, name, fileName) overload to include a filename.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.8/Petstore/src/Org.OpenAPITools/Api/PetApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net4.8/Petstore/src/Org.OpenAPITools/Api/PetApi.cs:2399">
P2: Multipart form fields are sent as a single unnamed FormUrlEncodedContent part. Multipart/form-data binding expects individual parts with Content-Disposition name parameters; an unnamed urlencoded blob can prevent binding of fields like name/status/additionalMetadata.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.7/Petstore/src/Org.OpenAPITools/Api/PetApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net4.7/Petstore/src/Org.OpenAPITools/Api/PetApi.cs:2645">
P2: Multipart file parts are added without a filename; ASP.NET Core treats parts without `filename` as form fields, so file parameters may not bind. Provide a filename when adding StreamContent.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net8/FormModels/src/Org.OpenAPITools/Api/PetApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net8/FormModels/src/Org.OpenAPITools/Api/PetApi.cs:2403">
P2: Non-file form fields are still bundled into a single unnamed `FormUrlEncodedContent` part in multipart/form-data. Multipart binding expects each field as its own part with a `name` parameter, so this aggregate unnamed part can cause `additionalMetadata`/`name`/`status` to be ignored by the server.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 186 files (changes from recent commits).

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/resources/csharp/libraries/generichost/OperationDataType.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/csharp/libraries/generichost/OperationDataType.mustache:1">
P2: Optional container file parameters are generated without nullability marker because the `isContainer` file branch omits `NullConditionalParameter`.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.7/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net4.7/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs:4315">
P2: GET request content is assigned unconditionally even when there are no form parameters, causing empty-body GET requests that may be rejected by strict HTTP infrastructure.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 55 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/client/petstore/csharp/generichost/net8/Petstore/src/Org.OpenAPITools/Api/PetApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net8/Petstore/src/Org.OpenAPITools/Api/PetApi.cs:2398">
P2: Skipping FormUrlEncodedContent when no fields are set sends a POST with no body and no application/x-www-form-urlencoded content-type, which can break form-encoded endpoints when optional fields are omitted.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/csharp/libraries/generichost/api.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/csharp/libraries/generichost/api.mustache:552">
P2: Form-encoded requests with only optional fields now send no body and no Content-Type header when no fields are set, because content creation is skipped and content-type selection is disabled for `formParams`. This is a regression from prior behavior (empty FormUrlEncodedContent) and can break endpoints that require `application/x-www-form-urlencoded` even with an empty payload or signature calculation that expects a body.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9 issues found across 52 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/client/petstore/csharp/generichost/net9/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net9/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs:4324">
P2: GET requests with no optional form params now always send an empty application/x-www-form-urlencoded body. This is a behavioral regression from the removed guard and can break servers/proxies that reject GET bodies.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net9/SourceGeneration/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net9/SourceGeneration/src/Org.OpenAPITools/Api/FakeApi.cs:4326">
P2: GET requests now always attach a (possibly empty) form body even when no optional form parameters are set, which can break intermediaries/servers that reject GET-with-body.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net10/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net10/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs:4324">
P2: GET request now always sends form-url-encoded content even when no form parameters are set, changing request semantics and risking rejection by intermediaries/endpoints. Restore the guard so GETs without form params send no body.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.7/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net4.7/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs:4315">
P2: GET request now always includes a form-encoded body even when no form params are set, which is non-standard and can be rejected by servers/proxies. Restore the guard so GET requests without form fields do not send a body.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net8/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net8/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs:4324">
P2: Unconditionally attaching FormUrlEncodedContent on a GET request changes behavior when no optional form params are set, resulting in a GET request with an empty body and content-type header. This can break servers or intermediaries that reject GET bodies; restore the guard so content is only set when there are form fields.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net8/NullReferenceTypes/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net8/NullReferenceTypes/src/Org.OpenAPITools/Api/FakeApi.cs:4326">
P2: GET requests now always include a form-encoded body even when no optional form parameters are set. For TestEnumParametersAsync, both form fields are optional, so an empty list still results in an attached `FormUrlEncodedContent`, causing GET-with-body behavior that previously was avoided and can break servers/proxies that reject or mishandle such requests.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.7/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net4.7/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs:4315">
P2: GET request now always includes a form-encoded body even when no optional form parameters are set, which can break servers that reject GETs with bodies.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net8/SourceGeneration/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net8/SourceGeneration/src/Org.OpenAPITools/Api/FakeApi.cs:4326">
P2: GET request now always includes a form body, even when no optional form parameters are set. This introduces an empty GET body (with Content-Type/Content-Length headers) that can break interoperability with servers or proxies that reject GET bodies. Restore the guard so content is only set when form parameters exist.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net10/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net10/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs:4324">
P2: GET request now always includes a form-url-encoded body even when no optional form fields are set, which can cause compatibility issues with servers/proxies that reject GET bodies.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

12 issues found across 18 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/client/petstore/csharp/generichost/net10/SourceGeneration/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net10/SourceGeneration/src/Org.OpenAPITools/Api/FakeApi.cs:4641">
P2: GET form parameters are still collected but never attached to the request after removing FormUrlEncodedContent, so caller-supplied form fields are silently dropped.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/standard2.0/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1">
P1: GET request still builds form fields but no longer attaches them to the HTTP request, so form parameters are dropped.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.7/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1">
P2: Form parameters are collected but never sent in GET operations, so user-supplied form params are silently dropped.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net8/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1">
P2: Form parameters are collected for TestEnumParametersAsync but never attached to the GET request after removing FormUrlEncodedContent, so enum_form_string fields are silently dropped.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net10/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1">
P2: Form parameters for enum_form_string/enum_form_string_array are built but never sent after removing FormUrlEncodedContent in TestEnumParametersAsync.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net10/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1">
P2: Form parameters are collected in TestEnumParametersAsync but never attached to the GET request, so enum_form_* values are silently dropped.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net8/NullReferenceTypes/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net8/NullReferenceTypes/src/Org.OpenAPITools/Api/FakeApi.cs:4641">
P1: Form parameters are collected but never attached to the request, so caller-provided form fields are silently ignored.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.8/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1">
P2: Form parameters for GET operations are collected but never attached to the request, so application/x-www-form-urlencoded request bodies defined by the spec (e.g., /fake/jsonFormData) are now dropped.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.7/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1">
P2: GET operations now collect form parameters but never send them, so required values like param/param2 are silently dropped from the request.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net8/SourceGeneration/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net8/SourceGeneration/src/Org.OpenAPITools/Api/FakeApi.cs:4641">
P2: Form parameters are constructed but never sent for GET operations after removing FormUrlEncodedContent, so required inputs are silently dropped.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net9/SourceGeneration/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net9/SourceGeneration/src/Org.OpenAPITools/Api/FakeApi.cs:4641">
P2: Form parameters for TestEnumParametersAsync are collected but never attached to the GET request, so required form data is silently dropped.</violation>
</file>

<file name="samples/client/petstore/csharp/generichost/net4.8/Petstore/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1">
P2: GET form parameters are collected but never sent; the removal of FormUrlEncodedContent drops enum_form_string and enum_form_string_array from the request.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@devhl-labs
Copy link
Contributor Author

devhl-labs commented Mar 9, 2026

cubic-dev-ai has provided good feedback, but its now stuck in a circle, nothing I do will satisfy it. I think further AI comments can be ignored. Here is the summary from copilot

The bot's recommendation has changed with every review cycle:

  1. Commit c5ad249 — Unconditional FormUrlEncodedContent assignment breaks GET requests. Recommendation: add a Count > 0 guard.
  2. Commit f0be364Count > 0 guard breaks POST requests (no Content-Type when all params are optional). Recommendation: remove guard, use unconditional assignment.
  3. Commit 547771d — Unconditional assignment breaks GET requests again. Recommendation: wrap in {{#isBodyAllowed}} to skip body on GET/DELETE/HEAD.
  4. Commit 0d6d381{{#isBodyAllowed}} silently drops GET form params (the petstore spec defines form params on GET /fake). Recommendation: add Count > 0 guard.

Round 4 is identical to round 1. The bot has gone in a circle.

Conclusion

The current implementation — if (formParameterLocalVars.Count > 0) — is correct.

The bot's concern in round 2 (POST loses Content-Type when Count is 0) is not a real problem: if there are no form params to send, sending an empty body with a Content-Type header is meaningless and incorrect behavior. Any POST operation that has required form params will always have Count > 0. Any POST with only optional params and none provided should not send a body at all.

The Count > 0 guard is the only approach that satisfies all legitimate constraints:

  • Sends the body (with correct Content-Type) when form params are present, regardless of HTTP method.
  • Omits the body when no params are actually set, avoiding an empty application/x-www-form-urlencoded body on any method.
  • Respects the spec's authority to define form params on unconventional methods like GET.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 52 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/client/petstore/csharp/generichost/net4.7/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs">

<violation number="1" location="samples/client/petstore/csharp/generichost/net4.7/FormModels/src/Org.OpenAPITools/Api/FakeApi.cs:4316">
P1: GET request now attaches a FormUrlEncodedContent body, which causes ProtocolViolationException in .NET Framework HttpClient/HttpWebRequest. This will crash when form parameters are supplied.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] CSharp code generator logic error [BUG][CSHARP][GENERICHOST] - Array of string/binary for multi-file uploads generates invalid code

3 participants