Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Mark catch-all route parameters as optional (#60392) #60544

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

mapedersen
Copy link

Fix: Catch-All Route Parameters Incorrectly Marked as Required in ApiExplorer

Description

This pull request addresses an issue in ASP.NET Core’s ApiExplorer where catch-all route parameters (e.g., {**catchAllParameter}) are erroneously marked as required, despite being optional in practice. At runtime, catch-all parameters match all values—including empty strings—so they should be considered optional. This discrepancy results in misleading API metadata, negatively impacting API documentation and client generation.

Changes Made

  • Added a new unit test in DefaultApiDescriptionProviderTest to validate that catch-all route parameters are reported as optional.
  • Followed a Test-Driven Development (TDD) approach: the test initially failed (confirming the bug) and then served as a baseline for the fix.
  • Updated the ProcessIsRequired method in DefaultApiDescriptionProvider:
    • Now examines the route template for a catch-all marker ({**).
    • If a catch-all marker is detected, the parameter’s IsRequired property is set to false.
    • Otherwise, the existing behavior is maintained.
  • Ensures that ApiExplorer metadata aligns with actual routing semantics.

Testing

  • The new unit test confirms that when a controller action defines a catch-all route parameter, it is correctly marked as optional.
  • The complete test suite was executed to verify that the changes do not negatively impact other aspects of ApiExplorer functionality.

Impact

  • This change is limited to API metadata generation logic and does not affect the runtime behavior of existing applications.
  • By accurately reflecting the optional nature of catch-all parameters, this fix improves:
    • The quality of generated API documentation (e.g., OpenAPI/Swagger).
    • The reliability of client libraries and automated testing tools by minimizing potential errors.

- 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.
@dotnet-issue-labeler dotnet-issue-labeler bot added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Feb 21, 2025
Copy link
Contributor

Thanks for your PR, @mapedersen. Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Feb 21, 2025
@mapedersen
Copy link
Author

@dotnet-policy-service agree

parameter.IsRequired = true;
// If the route template contains a catch-all parameter marker ("{**"), treat it as optional.
var template = context.ActionDescriptor.AttributeRouteInfo?.Template;
if (!string.IsNullOrEmpty(template) && template.Contains("{**"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this effectively end up setting IsRequired = false for route parameters that appear in a route with a catch-all? For example, /products/{category}/items/{group}/inventory/{**any} would end up treating category and group as optional with this logic?

I wonder if there is a way we can use the ApiParameterContext.RouteParameters property to check if a route template part is a catch-all and only set it for the matching parameter source.

How does that sound?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants