Skip to content

Fix #3153: serialize custom values in the UI ConfigObject AdditionalItems - #4132

Merged
martincostello merged 2 commits into
domaindrivendev:masterfrom
RaphaelFakhri:fix/3153-uiconfig-additionalitems
Sep 29, 2026
Merged

martincostello merged 2 commits into
domaindrivendev:masterfrom
RaphaelFakhri:fix/3153-uiconfig-additionalitems

Conversation

@RaphaelFakhri

Copy link
Copy Markdown
Contributor

Fixes #3153.

This is not fixed by the Microsoft.OpenApi v2 move, which only changed how the OpenAPI document is serialized. The failure is in the UI config serialization and still happens on master. With c.ConfigObject.AdditionalItems.Add("theme", new { colors = new { primary = "#086eaa" } }), exercised through the real middleware:

NotSupportedException: JsonTypeInfo metadata for type '<>f__AnonymousType0`1[...]' was not
provided by TypeInfoResolver of type 'ReDocOptionsJsonContext'

and the identical failure from SwaggerUIOptionsJsonContext.

Both middlewares serialize ConfigObject with the source-generated context alone whenever the application has not supplied its own JsonSerializerOptions. #2890 whitelisted primitives in those contexts, but AdditionalItems is [JsonExtensionData], so any complex value in it has no metadata and throws.

When RuntimeFeature.IsDynamicCodeSupported, the default options now chain a reflection-based resolver after the source-generated one:

TypeInfoResolver = JsonTypeInfoResolver.Combine(
    ReDocOptionsJsonContext.Default,
    new DefaultJsonTypeInfoResolver())

Registered types still resolve through source generation first, so the emitted config is byte-identical for everything that worked before, including the camelCase naming. On native AoT the resolver stays source-generated only, so AoT support is untouched. Application-supplied JsonSerializerOptions still win, as they did. No public API change.

Two regression tests, one per middleware, styled after the adjacent splice tests. Against master's source both fail with the exception above. With the change, Swashbuckle.AspNetCore.IntegrationTests is 224 passed, 0 failed on net8.0.

The tradeoff worth stating: RuntimeFeature.IsDynamicCodeSupported distinguishes native AoT, not trimming, so in a trimmed-but-JIT application a user's custom config type could in principle be trimmed away and serialize incompletely. That is the same exposure as the pre-6.6 behaviour this restores, and it matches the suppression pattern already used in these two files.

@codecov

codecov Bot commented Aug 25, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.11%. Comparing base (d0b1e26) to head (2d8b7b5).
⚠️ Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
...rc/Swashbuckle.AspNetCore.ReDoc/ReDocMiddleware.cs 90.00% 1 Missing ⚠️
...buckle.AspNetCore.SwaggerUI/SwaggerUIMiddleware.cs 90.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4132      +/-   ##
==========================================
- Coverage   95.24%   95.11%   -0.13%     
==========================================
  Files         111      111              
  Lines        4142     4158      +16     
  Branches      848      850       +2     
==========================================
+ Hits         3945     3955      +10     
- Misses        197      203       +6     
Flag Coverage Δ
Linux 95.11% <90.00%> (-0.13%) ⬇️
Windows 95.11% <90.00%> (-0.13%) ⬇️
macOS 95.11% <90.00%> (-0.13%) ⬇️

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.

@RaphaelFakhri

Copy link
Copy Markdown
Contributor Author

On the codecov patch check: the two uncovered lines are the return null; inside if (!RuntimeFeature.IsDynamicCodeSupported), one in each middleware. That branch only runs under native AoT, so the normal test run cannot reach it, and covering it would mean publishing an AoT app in the test matrix purely to observe a null.

The other side of the branch, the combined resolver, is what the two new tests exercise, and the AoT behaviour is unchanged from before this PR: the resolver stays source-generated only, exactly as it is on master today. Happy to restructure it if you would rather see that path covered some other way.

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.

🟢 Approval recommended

The resolver fallback directly addresses the reported failure with focused regression coverage for both middlewares.

Pull request overview

Fixes #3153 by enabling serialization of complex custom UI configuration values while preserving native AOT behavior.

Changes:

  • Adds reflection fallback resolvers when dynamic code is supported.
  • Preserves application-provided serializer options and source-generated native AOT paths.
  • Adds Swagger UI and ReDoc regression tests.
File summaries
File Description
src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIMiddleware.cs Adds Swagger UI serialization fallback.
src/Swashbuckle.AspNetCore.ReDoc/ReDocMiddleware.cs Adds ReDoc serialization fallback.
test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerUIIntegrationTests.cs Tests complex Swagger UI additional items.
test/Swashbuckle.AspNetCore.IntegrationTests/ReDocIntegrationTests.cs Tests complex ReDoc additional items.
Review details
  • Files reviewed: 4/4 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.

Comment thread src/Swashbuckle.AspNetCore.ReDoc/ReDocMiddleware.cs Outdated
Comment thread src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIMiddleware.cs Outdated
Comment thread test/Swashbuckle.AspNetCore.IntegrationTests/ReDocIntegrationTests.cs Outdated
Comment thread test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerUIIntegrationTests.cs Outdated
…ect AdditionalItems

Both middlewares serialized ConfigObject with the source-generated context alone
whenever the application had not supplied its own JsonSerializerOptions, so any
complex or anonymous value in the AdditionalItems extension data threw
NotSupportedException at request time.

When dynamic code is supported, chain a reflection-based resolver after the
source-generated one. Registered types still resolve through source generation
first, so the emitted config is unchanged, and on native AoT the resolver stays
source-generated only.
@RaphaelFakhri

Copy link
Copy Markdown
Contributor Author

Both suggestions are applied: the shortened UnconditionalSuppressMessage justifications in SwaggerUIMiddleware and ReDocMiddleware, and the removed issue-link comments in the two tests.

On the codecov patch check: the two uncovered lines are the return null; inside if (!RuntimeFeature.IsDynamicCodeSupported) in each middleware. That branch only runs under native AOT, which the test suite does not publish. Behavior there is unchanged from master.

@RaphaelFakhri
RaphaelFakhri force-pushed the fix/3153-uiconfig-additionalitems branch from f7bca47 to 2d8b7b5 Compare September 29, 2026 10:14
@martincostello martincostello added this to the v10.2.4 milestone Sep 29, 2026
@martincostello
martincostello merged commit 4220da9 into domaindrivendev:master Sep 29, 2026
13 of 14 checks passed
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

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 🚀

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: AdditionalItems serialization issue still present

3 participants