Skip to content

Support multiple response types per status code - #4100

Merged
martincostello merged 2 commits into
domaindrivendev:masterfrom
DeagleGross:deaglegross-csharp-unions-support
Aug 5, 2026
Merged

martincostello merged 2 commits into
domaindrivendev:masterfrom
DeagleGross:deaglegross-csharp-unions-support

Conversation

@DeagleGross

Copy link
Copy Markdown
Contributor

Group SupportedResponseTypes by status code in SwaggerGenerator and merge the schemas for each content type into an anyOf when more than one distinct type is reported. Previously a second response type for the same status code caused OpenApiResponses.Add to throw an ArgumentException (#2413, #2743).

This PR aligns with net11 aspnetcore changes - see dotnet/aspnetcore#55412 and dotnet/aspnetcore#56177 where the framework can surface multiple response types for a single status code. Moreover, this is a prerequisite for .NET11 C# unions. The single-response path is unchanged, so existing documents are byte for byte identical.

Adds unit tests plus a MultipleResponseTypes integration fixture that injects a second response type via a custom IApiDescriptionProvider (the API explorer collapses duplicates on net8-10), with snapshots verifying the generated document and that Kiota and NSwag generate and compile valid clients from the anyOf schema.

Fixes #2413
Fixes #2743

DeagleGross and others added 2 commits August 5, 2026 10:23
Group SupportedResponseTypes by status code in SwaggerGenerator and merge
the schemas for each content type into an `anyOf` when more than one
distinct type is reported. Previously a second response type for the same
status code caused OpenApiResponses.Add to throw an ArgumentException
(domaindrivendev#2413, domaindrivendev#2743).

This pre-fixes Swashbuckle for .NET 11 C# unions, where the framework can
surface multiple response types (or union cases) for a single status code.
The single-response path is unchanged, so existing documents are byte for
byte identical.

Adds unit tests plus a MultipleResponseTypes integration fixture that
injects a second response type via a custom IApiDescriptionProvider (the
API explorer collapses duplicates on net8-10), with snapshots verifying the
generated document and that Kiota and NSwag generate and compile valid
clients from the anyOf schema.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7722de8a-3ea5-40fd-999a-4f647ce8f4f7

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds first-class handling for multiple distinct response types per HTTP status code in SwaggerGenerator, emitting a single response entry whose per-content-type schema becomes an anyOf when needed. This prevents OpenApiResponses.Add collisions when ASP.NET Core surfaces multiple ApiResponseType entries for the same status code (notably aligned with upcoming .NET 11 behavior).

Changes:

  • Group SupportedResponseTypes by status code and merge schemas per content type into a single response using anyOf when multiple distinct types are present.
  • Add unit coverage for merging, deduplication, and ignoring typeless (void) responses during merge.
  • Add an end-to-end integration fixture (MultipleResponseTypes) plus updated snapshots verifying output and client-generation compatibility (Kiota + NSwag).

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.

Show a summary per file
File Description
test/WebSites/MultipleResponseTypes/Program.cs Minimal app + custom IApiDescriptionProvider to inject a second response type for the same status code.
test/WebSites/MultipleResponseTypes/MultipleResponseTypes.csproj New website project for integration testing the multi-response behavior.
test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/SwaggerGeneratorTests.cs Unit tests validating anyOf merge behavior, schema dedupe, and void-response handling.
test/Swashbuckle.AspNetCore.IntegrationTests/VerifyTests.cs Adds the new website to JSON validity snapshot verification.
test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs Adds the new website to middleware integration coverage.
test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/code/GeneratesValidClient_9af902faded38cfc/Models/Dog.verified.cs Kiota-generated model snapshot for the new response type.
test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/code/GeneratesValidClient_9af902faded38cfc/Models/Cat.verified.cs Kiota-generated model snapshot for the existing response type.
test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/code/GeneratesValidClient_9af902faded38cfc/KiotaOpenApiClient.verified.cs Kiota client snapshot validating composed response generation.
test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/code/GeneratesValidClient_9af902faded38cfc/Animals/Item/AnimalsItemRequestBuilder.verified.cs Kiota request builder snapshot showing composed (anyOf) response wrapper usage.
test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/code/GeneratesValidClient_9af902faded38cfc/Animals/AnimalsRequestBuilder.verified.cs Kiota request builder snapshot for the new endpoint path.
test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/code/GeneratesValidClient_653e9faaca680294/NSwagOpenApiClient.verified.cs NSwag client snapshot validating code generation/compilation against anyOf.
test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/8_0/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MultipleResponseTypes.Program_swaggerRequestUri=v1.verified.txt OpenAPI JSON snapshot (net8) showing anyOf for 200 response schema.
test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/9_0/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MultipleResponseTypes.Program_swaggerRequestUri=v1.verified.txt OpenAPI JSON snapshot (net9) showing anyOf for 200 response schema.
test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/10_0/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MultipleResponseTypes.Program_swaggerRequestUri=v1.verified.txt OpenAPI JSON snapshot (net10) showing anyOf for 200 response schema.
src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs Core implementation: group responses by status code and merge schemas per content type into anyOf when needed.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov

codecov Bot commented Aug 5, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.21%. Comparing base (4fa2d81) to head (6ba8a90).
⚠️ Report is 5 commits behind head on master.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4100      +/-   ##
==========================================
+ Coverage   95.19%   95.21%   +0.01%     
==========================================
  Files         111      111              
  Lines        4101     4115      +14     
  Branches      838      843       +5     
==========================================
+ Hits         3904     3918      +14     
  Misses        197      197              
Flag Coverage Δ
Linux 95.21% <100.00%> (+0.01%) ⬆️
Windows 95.21% <100.00%> (+0.01%) ⬆️
macOS 95.21% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@martincostello

Copy link
Copy Markdown
Collaborator

This is great, thanks @DeagleGross!

@martincostello
martincostello merged commit 00d5017 into domaindrivendev:master Aug 5, 2026
14 checks passed
DeagleGross added a commit to DeagleGross/Swashbuckle.AspNetCore that referenced this pull request Aug 5, 2026
…nto union schema support

Branch 1 (multiple response types per status code) was squash-merged upstream
as commit 00d5017. This branch still carried the original branch-1 commit
(c9d12cf) plus the union schema work, so merging produced comment-only
conflicts in SwaggerGenerator.cs. Resolved by taking upstream/master's comment
wording (the code was identical). All union changes preserved: SchemaGenerator
TryGenerateUnionSchema, JsonSerializerDataContractResolver TryGetUnionCaseTypes,
the union tests, net11 TFM additions, and the global.json net11 preview pin.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7722de8a-3ea5-40fd-999a-4f647ce8f4f7
@DeagleGross
DeagleGross deleted the deaglegross-csharp-unions-support branch August 5, 2026 13:55
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Thanks for your contribution @DeagleGross - the changes from this pull request have been published as part of version 10.3.0 📦, which is now available from NuGet.org 🚀

This was referenced Oct 11, 2026
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.

Cannot have multiple response types for same status code Cannot have multiple response types for same status code

3 participants