Avoid ArgumentException when Problem/ValidationProblem extensions conflict with defaults#67690
Avoid ArgumentException when Problem/ValidationProblem extensions conflict with defaults#67690UditDewan wants to merge 7 commits into
Conversation
…flict with defaults ControllerBase.Problem and ControllerBase.ValidationProblem merged caller-supplied extensions into the ProblemDetails instance with IDictionary.Add, which throws ArgumentException when a key (e.g. "traceId") was already populated by the ProblemDetailsFactory. Use the indexer instead so explicitly passed extensions override the defaults. Fixes dotnet#66407
|
Thanks for your PR, @UditDewan. Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
|
@dotnet-policy-service agree |
There was a problem hiding this comment.
Pull request overview
This PR fixes a runtime ArgumentException that can occur when callers pass extensions to ControllerBase.Problem / ControllerBase.ValidationProblem using keys already populated by ProblemDetailsFactory (notably traceId). Instead of throwing on duplicate keys, caller-supplied values now override the pre-populated defaults.
Changes:
- Update
ControllerBase.Problemto mergeextensionsvia the dictionary indexer (overwrite) rather thanIDictionary.Add(throw on duplicate). - Update
ControllerBase.ValidationProblemto use the same overwrite merge behavior. - Add regression tests verifying that passing a conflicting
traceIdextension does not throw and results in the caller’s value being used.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/Mvc/Mvc.Core/src/ControllerBase.cs | Switch extension merge behavior to overwrite duplicates (avoids ArgumentException on keys like traceId). |
| src/Mvc/Mvc.Core/test/ControllerBaseTest.cs | Add tests confirming caller-supplied traceId overrides factory-provided values for both Problem and ValidationProblem. |
DeagleGross
left a comment
There was a problem hiding this comment.
Thanks for the fix! Minimal API has similar behavior in Results
aspnetcore/src/Http/Http.Results/src/Results.cs
Lines 818 to 826 in 81a85fc
and Typed Results
aspnetcore/src/Http/Http.Results/src/TypedResults.cs
Lines 872 to 880 in 81a85fc
Can you please change code there as well and cover with tests (ResultsTests.cs and TypedResultsTests.cs)? Thanks!
|
I'm not honestly sure if this is a change we want to do. What's the use case of user needing to write a value with the same key as ASP.NET Core? Giving that ability to users might mean that they eliminate assumptions on well-known keys that we add and what values do we expect to be there. |
Results.ValidationProblem and TypedResults.Problem/ValidationProblem copied caller-supplied extensions with IDictionary.Add, which throws on duplicate keys in the input enumerable. Use the indexer for consistency with ControllerBase.Problem and ControllerBase.ValidationProblem.
…UditDewan/aspnetcore into fix-problem-extensions-conflict
|
The main issue is the failure mode, not necessarily the ability to override framework values. Rn, passing a key like Users can already replace well-known keys through This PR just makes the explicit |
halter73
left a comment
There was a problem hiding this comment.
Approving. This should generally be deterministic, and fields like traceId will usually be added automatically, but extension names have never been documented as reserved.
The exception might expose an unnecessary user-supplied value during development, but I don't think justifies failing the request after the user code has already returned. Overwriting is a more useful and consistent behavior.
Make caller-supplied ProblemDetails extensions override framework defaults instead of throwing.
Description
ControllerBase.ProblemandControllerBase.ValidationProblemmerge the caller-suppliedextensionsdictionary into the createdProblemDetailswithIDictionary.Add. WhenProblemDetailsFactoryhas already populated a key with the same name (for exampletraceId, whichDefaultProblemDetailsFactoryalways sets), the call throwsArgumentExceptionat runtime. Callers have no way to know which keys the factory pre-populates, so passing common keys liketraceIdortimestampcurrently crashes the request.This change merges the entries with the dictionary indexer instead, so an explicitly passed extension overrides the default value rather than throwing. Existing behavior for non-conflicting keys is unchanged. Added regression tests covering both methods.
Fixes #66407