Preserve IOutboundParameterTransformer on optional route constraints - #65578
Preserve IOutboundParameterTransformer on optional route constraints#65578kubaflo wants to merge 1 commit into
Conversation
|
Thanks for your PR, @@kubaflo. Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
There was a problem hiding this comment.
Pull request overview
This PR addresses issue #23063 — when a route constraint implements IOutboundParameterTransformer and the route parameter is optional, wrapping the constraint in OptionalRouteConstraint caused the transformer to be silently dropped, resulting in incorrect URL generation.
Changes:
- Introduced
OptionalOutboundParameterTransformerRouteConstraint, an internal class that extendsOptionalRouteConstraintwhile also delegatingTransformOutboundto the inner constraint. - Updated
DefaultParameterPolicyFactory.InitializeRouteConstraintto use the new class when the inner constraint implementsIOutboundParameterTransformer. - Added agentic workflow skill files (
fix-issue,write-tests,verify-tests,try-fix,ai-summary-commentSKILL.md and associated scripts/tests) — unrelated to the routing fix.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/Http/Routing/src/Constraints/OptionalOutboundParameterTransformerRouteConstraint.cs |
New internal class that preserves IOutboundParameterTransformer behavior through optional constraint wrapping |
src/Http/Routing/src/DefaultParameterPolicyFactory.cs |
Selects new wrapper class when inner constraint implements IOutboundParameterTransformer |
src/Http/Routing/test/UnitTests/DefaultParameterPolicyFactoryTest.cs |
Regression test for the bug fix, including a TransformingRouteConstraint helper |
.github/skills/fix-issue/SKILL.md |
Agentic workflow definition (5-phase issue fixer) |
.github/skills/fix-issue/tests/test-skill-definition.sh |
Tests for the fix-issue SKILL.md — several assertions fail against current SKILL.md content |
.github/skills/fix-issue/tests/test-ai-summary-comment.sh |
Tests for the ai-summary-comment scripts — references non-existent post-try-fix-comment.sh |
.github/skills/ai-summary-comment/SKILL.md |
Skill definition for posting PR progress comments |
.github/skills/ai-summary-comment/scripts/post-ai-summary-comment.sh |
Shell script that posts/updates the AI summary comment on a PR |
.github/skills/write-tests/SKILL.md |
Skill definition for writing unit tests |
.github/skills/verify-tests/SKILL.md |
Skill definition for verifying tests |
.github/skills/try-fix/SKILL.md |
Skill definition for attempting alternative fixes |
When a route constraint implements IOutboundParameterTransformer, wrapping it in OptionalRouteConstraint loses the transformer capability because OptionalRouteConstraint does not implement the interface. Add OptionalOutboundParameterTransformerRouteConstraint that extends OptionalRouteConstraint and delegates TransformOutbound to the inner constraint. DefaultParameterPolicyFactory.InitializeRouteConstraint now uses this wrapper when the inner constraint is a transformer. Fixes dotnet#23063 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
d498fe7 to
d0791a2
Compare
|
Looks like this PR hasn't been active for some time and the codebase could have been changed in the meantime. |
🤖 AI Summary
🔍 Automated Fix Report
🔍 Pre-Flight — Context & Validation
Issue: #23063 —
TransformOutboundnot called for optional route segments with parameter transformersArea:
src/Http/Routing/Root Cause: When a route constraint is marked optional,
DefaultParameterPolicyFactorywraps it inOptionalRouteConstraint, which only implementsIRouteConstraint— losing theIOutboundParameterTransformerinterface. TheTemplateBinderchecks forIOutboundParameterTransformerseparately, so the transformer is never invoked for optional segments.Classification: Bug — interface loss during constraint wrapping
🧪 Test — Bug Reproduction
Test File:
src/Http/Routing/test/UnitTests/Constraints/OptionalOutboundParameterTransformerRouteConstraintTest.csTests Added:
Match_DelegatesToInnerConstraint_WhenValuePresentMatch_ReturnsTrue_WhenValueIsOptionalOrEmptyTransformOutbound_DelegatesToInnerTransformerStrategy: Verified that the new wrapper constraint correctly delegates both
IRouteConstraint.MatchandIOutboundParameterTransformer.TransformOutboundto the inner constraint.🚦 Gate — Test Verification & Regression
Gate Result: ✅ All 21 routing constraint tests pass
Test Command:
Regression: No failures in existing test suite.
🔧 Fix — Analysis & Comparison (✅ 2 passed, ❌ 1 failed)
Fix: Preserve
IOutboundParameterTransformerinterface when wrapping route constraints inOptionalRouteConstraint.OptionalOutboundParameterTransformerRouteConstraintclassDefaultParameterPolicyFactoryInnerConstraintat 3 consumer sites✅ Attempt 0: PASS
Approach: New
OptionalOutboundParameterTransformerRouteConstraintclass extendingOptionalRouteConstraint+ implementingIOutboundParameterTransformer.Created a new internal class that preserves both interfaces. Modified
DefaultParameterPolicyFactory.InitializeRouteConstraintto detectIOutboundParameterTransformerand use the specialized wrapper.📄 Diff
❌ Attempt 1: FAIL
Approach: Private nested
OptionalTransformerConstraintclass insideDefaultParameterPolicyFactory.Instead of a separate file, use a private nested class that implements both
IRouteConstraintandIOutboundParameterTransformer, composingOptionalRouteConstraintinternally. Test fails because it checks concrete typeOptionalOutboundParameterTransformerRouteConstraint— would need test modification.📄 Diff
✅ Attempt 2: PASS
Approach: Unwrap
OptionalRouteConstraint.InnerConstraintat all consumer sites (TemplateBinder, DefaultTemplateBinderFactory, DfaMatcherBuilder).Instead of creating a new wrapper class, check at each site where
IOutboundParameterTransformeris queried whether the policy is anOptionalRouteConstraintwhoseInnerConstraintimplementsIOutboundParameterTransformer, and unwrap it. No new types needed, but touches 3 consumer files.📄 Diff