Skip to content

Commit eec0e99

Browse files
committed
Fix: Mark catch-all route parameters as optional (#60392)
- Added a unit test in DefaultApiDescriptionProviderTest to validate that catch-all route parameters (e.g., "{**catchAllParameter}") are reported as optional. - Modified ProcessIsRequired in DefaultApiDescriptionProvider to detect catch-all parameters (by checking for the "{**" pattern in the route template) and mark them as not required. - This change aligns ApiExplorer metadata with the actual runtime behavior, ensuring more accurate API documentation. - Follows TDD practices by first writing a failing test and then implementing the fix.
1 parent a2e3677 commit eec0e99

File tree

2 files changed

+28
-2
lines changed

2 files changed

+28
-2
lines changed

src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,9 +315,18 @@ internal static void ProcessIsRequired(ApiParameterContext context, MvcOptions m
315315
parameter.IsRequired = true;
316316
}
317317

318-
if (parameter.Source == BindingSource.Path && parameter.RouteInfo != null && !parameter.RouteInfo.IsOptional)
318+
if (parameter.Source == BindingSource.Path && parameter.RouteInfo != null)
319319
{
320-
parameter.IsRequired = true;
320+
// If the route template contains a catch-all parameter marker ("{**"), treat it as optional.
321+
var template = context.ActionDescriptor.AttributeRouteInfo?.Template;
322+
if (!string.IsNullOrEmpty(template) && template.Contains("{**"))
323+
{
324+
parameter.IsRequired = false;
325+
}
326+
else if (!parameter.RouteInfo.IsOptional)
327+
{
328+
parameter.IsRequired = true;
329+
}
321330
}
322331
}
323332
}

src/Mvc/Mvc.ApiExplorer/test/DefaultApiDescriptionProviderTest.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,23 @@ namespace Microsoft.AspNetCore.Mvc.Description;
3232

3333
public class DefaultApiDescriptionProviderTest
3434
{
35+
[Fact]
36+
public void CatchAllParameter_IsReportedAsOptional()
37+
{
38+
// Arrange: Create an action descriptor with a catch-all route template.
39+
var action = CreateActionDescriptor();
40+
action.AttributeRouteInfo = new AttributeRouteInfo { Template = "{**catchAllParameter}" };
41+
42+
// Act: Get the API descriptions using the existing helper.
43+
var descriptions = GetApiDescriptions(action);
44+
45+
// Assert: There should be one description, with one parameter named "catchAllParameter"
46+
// and its IsRequired property should be false.
47+
var description = Assert.Single(descriptions);
48+
var parameter = Assert.Single(description.ParameterDescriptions, p => p.Name == "catchAllParameter");
49+
Assert.False(parameter.IsRequired);
50+
}
51+
3552
[Fact]
3653
public void GetApiDescription_IgnoresNonControllerActionDescriptor()
3754
{

0 commit comments

Comments
 (0)