-
Notifications
You must be signed in to change notification settings - Fork 10.1k
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
Removing from API Description parameters when inferred (FromPath) and not in the route #39607
Removing from API Description parameters when inferred (FromPath) and not in the route #39607
Conversation
As mentioned in the PR description, I am changing only the |
What's the inferred binding source for |
That is exactly what it does, it set as |
@brunolins16 Can you educate me about where the InferParameterBinderConvention gets called before the changeset here gets called? I just had a thought about whether or not it is possible to tweak that logic in the convention without disabling it outright but not sure if it has access to all the state we need to decide the binding source for the buggy scenario. |
@captainsafia Sure. Here is what I found. It is called from aspnetcore/src/Mvc/Mvc.Core/src/ApplicationModels/ApiBehaviorApplicationModelProvider.cs Line 48 in 1ca0709
aspnetcore/src/Mvc/Mvc.Core/src/ApplicationModels/ApiBehaviorApplicationModelProvider.cs Line 80 in 1ca0709
aspnetcore/src/Mvc/Mvc.Core/src/ApplicationModels/ControllerActionDescriptorProvider.cs Line 85 in 1ca0709
The
aspnetcore/src/Mvc/Mvc.Core/src/Infrastructure/DefaultActionDescriptorCollectionProvider.cs Line 47 in 1ca0709
I tried to find a way to not process the |
@brunolins16 Ah, I see. I was thinking about making a change somewhere here but I don't believe that we have access to the aspnetcore/src/Mvc/Mvc.Core/src/ApplicationModels/InferParameterBindingInfoConvention.cs Lines 63 to 76 in 6850cc8
|
If I understood your idea, that is exactly what it does here (but to find if the parameter is part of any route and set all as FromRoute) aspnetcore/src/Mvc/Mvc.Core/src/ApplicationModels/InferParameterBindingInfoConvention.cs Line 109 in 6850cc8
The problem, that I was not able to figure out, is because the ActionDescriptors (InferParameterBindingInfoConvention) could be populated before the ApiDescriptionProviders we don't have any way to change them other them re-populating that will affect the execution. |
@pranavkm Do you happen to know what is the reason why we set all of them FromRoute if it appears in one of the routes? My question is to understand what the expected binding behavior is for an action like this: [Route("foo")]
[Route("bar/{id}")]
public IActionResult Get(int id = 0); Today the following is happening, and I don't feel it is completely right.
|
We'd wanted the rules to be easy to reason about. It's fairly unusual to have a parameter be bound from the route for one template but not the other. That said, it would be a breaking change to tweak this behavior particularly since nobody's really complained about the runtime behavior. |
Got it, so, in this case my fix is wrong because what should happen in this case, if I understood correctly, is the API explorer not include the parameter as part of the API description, is that right? |
I don't know if it should not be included, but in the test:
I would expect the inferred and expected binding source to be the same for all 3. If enough people complained that |
I got the binding behavior, however, if I have a controller like this [HttpGet("api/products", Name = "Foo")]
[HttpGet("api/products/{id}", Name = "Bar")]
public IActionResult Get(int id = 0); And use the
This in my opinion, is wrong because the id will never be bound in the Maybe it is an unusual scenario and, also, the API name will help the client, however, I still feel that it is wrong. |
Ok, that makes sense. Assuming the code doesn't end up being too gnarly, we could consider it. |
src/Mvc/Mvc.ApiExplorer/test/DefaultApiDescriptionProviderTest.cs
Outdated
Show resolved
Hide resolved
src/Mvc/Mvc.ApiExplorer/test/DefaultApiDescriptionProviderTest.cs
Outdated
Show resolved
Hide resolved
src/Mvc/Mvc.ApiExplorer/test/DefaultApiDescriptionProviderTest.cs
Outdated
Show resolved
Hide resolved
@@ -258,6 +262,20 @@ private void ProcessRouteParameters(ApiParameterContext context) | |||
parameter.Source = BindingSource.Path; | |||
} | |||
} | |||
else | |||
{ | |||
if (parameter.Source == BindingSource.Path && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: else if
?
// probably because another route to this action contains it as route parameter and | ||
// will be removed from the API description | ||
// https://github.com/dotnet/aspnetcore/issues/26234 | ||
context.Results.RemoveAt(i); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this situation only occur for parameters with default values? Given the scenario in the issue,
[HttpGet]
[Route("foo")]
[Route("bar/{id}")]
public IActionResult Get(int id = 0)
id
could never come from the query string for the foo
route, could it? Assuming not, this looks good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not because we stamp in id
's BindingSource as FromRoute
due to this - https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/ApplicationModels/InferParameterBindingInfoConvention.cs#L109-L126 (essentially there's one ParameterInfo for all of the route templates, so we treat the templates as a union)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to include an example: #39607 (comment)
Co-authored-by: Stephen Halter <halter73@gmail.com>
Changing from Path to ModelBinding when inferred API parameters
Description
The
InferParameterBindingInfoConvention
will follow the rule described here ApiBehaviorOptions.SuppressInferBindingSourcesForParameters Propertyaspnetcore/src/Mvc/Mvc.Core/src/ApplicationModels/InferParameterBindingInfoConvention.cs
Line 94 in 2325e12
However, it causes the issue #26234, since both will contain the parameter listed from route. With that in this PR I am adding a small check to verify if the parameter with
BindingSource == Path
and that does not contain aFromRoute
attribute is part of the current route and if not, it will be changed back toModelBinding
.This change will affect only the
APIExplorer
provider.Fixes #26234