Repository navigation
Fix #3169: emit form encoding for properties of a complex form parameter - #4128
Conversation
…lex form parameter The encoding map was built from the media type schema's own Properties, but a complex [FromForm] parameter is an unattached schema reference whose Properties proxy to a null target during generation, so its properties got no encoding entries at all. Arrays inside such an object then serialized comma joined, while arrays bound as flat parameters exploded. Resolve the reference through the schema repository, walking allOf, before building the map.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #4128 +/- ##
==========================================
+ Coverage 95.21% 95.23% +0.01%
==========================================
Files 111 111
Lines 4115 4132 +17
Branches 843 847 +4
==========================================
+ Hits 3918 3935 +17
Misses 197 197
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
🟢 Approval recommended
The focused implementation handles references, composition, and cycles while comprehensive regression and snapshot coverage confirms the intended output.
Pull request overview
Fixes complex form-bound array serialization by resolving referenced schema properties before generating OpenAPI encoding metadata.
Changes:
- Adds recursive
$ref/allOfproperty resolution with cycle protection. - Generates
style: formencoding for resolved complex-type properties. - Adds regression coverage and refreshes snapshots across supported frameworks.
File summaries
| File | Description |
|---|---|
src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs |
Uses resolved properties for form encoding. |
src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/OpenApiSchemaExtensions.cs |
Adds recursive property resolution. |
test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/SwaggerGeneratorTests.cs |
Tests complex array form encoding. |
test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TypeWithArrayProperty.cs |
Adds the regression model. |
test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/FakeController.cs |
Adds the regression endpoint fixture. |
test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/8_0/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject.verified.txt |
Updates .NET 8 form-object output. |
test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/8_0/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject_AndString.verified.txt |
Updates .NET 8 mixed-form output. |
test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/8_0/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithSeveralFromForms.verified.txt |
Updates .NET 8 multi-form output. |
test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/9_0/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject.verified.txt |
Updates .NET 9 form-object output. |
test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/9_0/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject_AndString.verified.txt |
Updates .NET 9 mixed-form output. |
test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/9_0/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithSeveralFromForms.verified.txt |
Updates .NET 9 multi-form output. |
test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/10_0/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject.verified.txt |
Updates .NET 10 form-object output. |
test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/10_0/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject_AndString.verified.txt |
Updates .NET 10 mixed-form output. |
test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/10_0/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithSeveralFromForms.verified.txt |
Updates .NET 10 multi-form output. |
test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/8_0/VerifyTests.TypesAreRenderedCorrectly.verified.txt |
Records .NET 8 resolved encodings. |
test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/8_0/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Program_swaggerRequestUri=v1.verified.txt |
Updates .NET 8 WebApi output. |
test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/9_0/VerifyTests.TypesAreRenderedCorrectly.verified.txt |
Records .NET 9 resolved encodings. |
test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/9_0/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Program_swaggerRequestUri=v1.verified.txt |
Updates .NET 9 WebApi output. |
test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/10_0/VerifyTests.TypesAreRenderedCorrectly.verified.txt |
Records .NET 10 resolved encodings. |
test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/10_0/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Program_swaggerRequestUri=v1.verified.txt |
Updates .NET 10 WebApi output. |
Review details
- Files reviewed: 20/20 changed files
- Comments generated: 0
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Thanks for your contribution @RaphaelFakhri - the changes from this pull request have been published as part of version 10.3.0 📦, which is now available from NuGet.org 🚀 |
Fixes #3169.
GenerateRequestBodyFromFormParametersbuilt the encoding map from the media type schema's own properties:When a form parameter is a complex type, that schema is an unattached
OpenApiSchemaReferencewhosePropertiesproxy to a null target while the document is still being generated, so its properties produced no encoding entries and the map came out empty. Parameters bound flat did getstyle: form, which is what makes Swagger UI explode an array into repeated fields, so an array behaved differently depending only on whether it sat inside a complex object.The properties are now resolved through
SchemaRepositorybefore the map is built, by a new internalResolvePropertiesextension that mirrors the existingResolveTypehelper: it follows the reference, walksallOf, guards against cycles with a visited-id set, and takes the first value on a key collision.Two things worth stating rather than leaving to be found in the diff.
The map now covers every
allOfmember and the schema's own properties, where the old expression took only the first member that had any. Every entry maps to the sameStyle = Form, so this is strictly broader coverage with identical values.This changes the emitted document for any application with a complex form parameter: those properties now get
style: formencoding entries they previously lacked. That is the fix, not a side effect.On the spec: OpenAPI 3.0 says an encoding key "MUST exist in the schema as a property". Here that is read as the resolved schema, which is the only coherent reading when the media type schema is a
$ref, since every consumer resolves the reference before applying encoding. It also matches the document produced by the workaround in the issue.A regression test in
SwaggerGeneratorTestspins the encoding entries for an array property on a complex form-bound type, and threeVerifyTestssnapshots in the SwaggerGen tests plus six integration snapshots gain encoding blocks. Every snapshot was regenerated from a real run rather than edited by hand.Swashbuckle.AspNetCore.SwaggerGen.Testis 761 passed, 0 failed on net8.0, net9.0 and net10.0, and the integrationVerifyTestsare 27 passed, 0 failed on all three.