Skip to content

Use Minimal APIs JSON options when MVC is not registered - #4116

Merged
martincostello merged 3 commits into
domaindrivendev:masterfrom
RaphaelFakhri:fix/3097-minimal-api-json-options
Oct 2, 2026
Merged

martincostello merged 3 commits into
domaindrivendev:masterfrom
RaphaelFakhri:fix/3097-minimal-api-json-options

Conversation

@RaphaelFakhri

@RaphaelFakhri RaphaelFakhri commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #3097.

IOptions<Microsoft.AspNetCore.Mvc.JsonOptions> resolves in any application, whether or not MVC was ever registered, so the existing fallback to IOptions<Microsoft.AspNetCore.Http.Json.JsonOptions> never ran: in a Minimal APIs application the MVC defaults were returned and the options set through ConfigureHttpJsonOptions were ignored. The generated document then contradicted what the endpoints actually serialize, which is the case in the report: the endpoint returns {"HelloWorld":"hi"} while the schema documents helloWorld.

The MVC options are now preferred only when MVC is actually registered, detected through IControllerFactory. I checked that it is registered by AddControllers(), AddMvcCore() and AddMvc(), and by nothing a Minimal APIs application uses, including AddEndpointsApiExplorer(), which is why IActionDescriptorCollectionProvider is not the marker here.

When an application registers both, MVC keeps precedence exactly as before: one document is generated with one set of serializer options, so a tie-break is unavoidable and this is the one that does not change existing behaviour. An application that configures neither is unaffected, since both option types default to the same web defaults.

Four tests in SwaggerGenServiceCollectionExtensionsTests: Minimal APIs only, MVC only, both registered, and neither configured. Swashbuckle.AspNetCore.SwaggerGen.Test passes on net8.0, net9.0 and net10.0 with 764 tests, 0 failed.

It turns out the repo's own Minimal APIs sample already demonstrates the bug: test/WebSites/WebApi adds a JsonStringEnumConverter through Microsoft.AspNetCore.Http.Json.JsonOptions and never registers MVC, but the committed snapshots document DateTimeKind as an int32 enum (0, 1, 2) while the endpoints actually serialize "Unspecified", "Utc" and "Local". With this change the generated document matches what the sample really returns, and the six snapshots are updated accordingly; every changed hunk is that one schema.

This deliberately takes the no-new-public-API route rather than reviving what #3378 attempted, since that PR was closed with an invitation to start fresh.

@codecov

codecov Bot commented Aug 25, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.24%. Comparing base (d0b1e26) to head (fdd4aa1).
⚠️ Report is 16 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #4116   +/-   ##
=======================================
  Coverage   95.24%   95.24%           
=======================================
  Files         111      111           
  Lines        4142     4143    +1     
  Branches      848      848           
=======================================
+ Hits         3945     3946    +1     
  Misses        197      197           
Flag Coverage Δ
Linux 95.24% <100.00%> (+<0.01%) ⬆️
Windows 95.24% <100.00%> (+<0.01%) ⬆️
macOS 95.24% <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.

@RaphaelFakhri
RaphaelFakhri force-pushed the fix/3097-minimal-api-json-options branch from 7a38cf1 to 6c2b100 Compare August 25, 2026 17:52
IOptions<Mvc.JsonOptions> resolves in any application, whether or not MVC was
registered, so the fallback to the Minimal APIs options never ran and MVC's own
camelCase default silently won. An application that sets a naming policy through
ConfigureHttpJsonOptions therefore got a document that contradicted what its
endpoints actually serialize.

Only prefer the MVC options when MVC is registered, detected through
IControllerFactory, which AddControllers, AddMvcCore and AddMvc all register and
nothing in a Minimal APIs application does. When both are registered MVC keeps
precedence, as before.
Each path now sets a policy that suffixes the property name, so an assertion
names the source of the options directly instead of relying on a null policy
looking different from the camelCase default. The both-registered test sets a
different policy on each side, so it shows MVC winning rather than only showing
camelCase.
The WebApi sample registers JsonStringEnumConverter through the Minimal APIs
JSON options, which are now used because the sample does not register MVC.
Regenerated from test runs.
@RaphaelFakhri

Copy link
Copy Markdown
Contributor Author

The branch is rebased onto current master and the change is now limited to SwaggerGenServiceCollectionExtensions and its test class.

One set of snapshots still changes, and it is a direct result of the fix rather than a wider code change. The WebApi sample registers JsonStringEnumConverter through Configure<JsonOptions> (Minimal APIs) and does not register MVC. Before this change, Swashbuckle read MVC's default options and ignored that registration, so the DateTimeKind enum was documented as integers even though the application serializes it as strings. The VerifyTests snapshots for WebApi (net8.0, net9.0, net10.0) and the Kiota and NSwag client snapshots generated from them now show the string values. The last commit contains only those regenerated files.

If you prefer that the sample stays unchanged, the alternative is to add AddControllers() to the sample, which keeps the old output but no longer exercises the Minimal APIs path.

@RaphaelFakhri
RaphaelFakhri force-pushed the fix/3097-minimal-api-json-options branch from 6c2b100 to fdd4aa1 Compare September 29, 2026 10:06
@martincostello martincostello added this to the v10.3.0 milestone Oct 2, 2026
@martincostello
martincostello merged commit 70ac2c7 into domaindrivendev:master Oct 2, 2026
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 15, 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]: Property casing policy is ignored when using minimal APIs, camel casing always applied

2 participants