Skip to content

Fix ApiExplorer to include FromQuery(Name) prefix in parameter names - #68344

Merged
Youssef1313 merged 3 commits into
dotnet:mainfrom
snemeckayova:dev/snemeckayova/fromquery-name-fix
Aug 19, 2026
Merged

Fix ApiExplorer to include FromQuery(Name) prefix in parameter names#68344
Youssef1313 merged 3 commits into
dotnet:mainfrom
snemeckayova:dev/snemeckayova/fromquery-name-fix

Conversation

@snemeckayova

Copy link
Copy Markdown
Contributor

When using [FromQuery(Name = "prefix")] on complex types, ApiExplorer was incorrectly omitting the prefix from parameter names in Swagger/OpenAPI documentation. This caused a mismatch between the documented parameter names and the actual query parameter names that ASP.NET Core's model binder expects.

For example, with [FromQuery(Name = "custom")] CustomType customType, the actual query parameter is custom.Input, but Swagger docs were showing just Input.

This fix ensures ApiExplorer includes the FromQuery(Name) prefix when generating parameter names, so Swagger documentation accurately reflects the actual binding behavior.

Fixes #43464

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes ApiExplorer’s parameter name generation so that when a complex action parameter has an explicit binder name via [FromQuery(Name = "...")], the generated parameter names (e.g., for Swagger/OpenAPI) include that prefix (e.g., custom.Input) to match actual model binding behavior.

Changes:

  • Update DefaultApiDescriptionProvider traversal to incorporate BinderModelName when building the container/prefix for complex parameters.
  • Adjust the existing ApiExplorer test to assert the prefixed query parameter name (employee.Name) for a complex [FromQuery(Name="employee")] parameter.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs Includes BinderModelName when computing the container name for complex models so prefixed query parameter names are produced.
src/Mvc/Mvc.ApiExplorer/test/DefaultApiDescriptionProviderTest.cs Updates assertion to validate the new prefixed parameter naming behavior.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1627 to 1629
var id = Assert.Single(description.ParameterDescriptions, p => p.Name == "employee.Name");
Assert.Same(BindingSource.Query, id.Source);
Assert.Equal(typeof(string), id.Type);
Assert.Single(description.ParameterDescriptions);

var id = Assert.Single(description.ParameterDescriptions, p => p.Name == "Name");
var id = Assert.Single(description.ParameterDescriptions, p => p.Name == "employee.Name");

@Youssef1313 Youssef1313 Aug 12, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What happens if Employee class had a property that had [FromHeader("X-MyCustomHeader")]?

Are we prefixing this as well? And is it expected to be prefixed? Same question applies to FromRoute

In addition, let's make sure we have equivalent tests for both Mvc and minimal API, including tests directed for OpenAPI

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If no scenario here is applicable for minimal API (e.g, an analyzer warning is produced), feel free to skip the minimal API part here.

@@ -635,7 +635,7 @@ private void Visit(

// We don't want to append the **parameter** name when building a model name.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's update this comment as well to reflect the updated condition.

And correct me if I'm wrong, I think modelMetadata.ContainerType != null is responsible for appending a property name, so it prevents appending parameter name. But then the other check can append a parameter name but if and only if explicitly given via IModelNameProvider.

var description = Assert.Single(descriptions);
Assert.Equal(2, description.ParameterDescriptions.Count);

var header = Assert.Single(description.ParameterDescriptions, p => p.Name == "employee.X-MyCustomHeader");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@snemeckayova Have you double checked the actual behavior of the API at runtime? Does it accept employee.X-MyCustomHeader? or should it be X-MyCustomHeader?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Checked it, at runtime the API accepts X-MyCustomHeader, not employee.X-MyCustomHeader, so this is basically wrong. Should I fix it under this PR?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, I think it will otherwise be a regression introduced in this PR.

Comment on lines +1649 to +1651
var route = Assert.Single(description.ParameterDescriptions, p => p.Name == "employee.employeeid");
Assert.Same(BindingSource.Path, route.Source);
Assert.Equal(typeof(string), route.Type);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same question here, but Path is a bit special as it relates to the placeholder in the template.

Does this parameter bind correctly for MapGet(/api/{employeeid}, ...)? Or for {employee.employee.id}? Or none of them actually binds?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

{employee.employeeid} binds; {employeeid} and {employee.employee.id} don't

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@snemeckayova Thanks for confirming. So only headers are special here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, that's correct

@Youssef1313
Youssef1313 self-requested a review August 19, 2026 12:05
@Youssef1313
Youssef1313 merged commit 4c2025b into dotnet:main Aug 19, 2026
27 checks passed
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 12.0-preview1 milestone Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ApiExplorer does not respect [FromQuery.Name] for complex parameter types

4 participants