EndpointMetadataApiDescriptionProvider Misses Route Parameters
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 281
Description
### Is there an existing issue for this?
- [X] I have searched the existing issues
### Describe the bug
`EndpointMetadataApiDescriptionProvider` misses creating `ApiDescriptionParameter` instances that are defined as `BindingSource.Custom` or `BindingSource.Special`. As seen [here](../blob/release/6.0/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs#L103), ``EndpointMetadataApiDescriptionProvider`` only considers parameter candidates via the `ParameterInfo` acquired through Reflection.
Some `BindingSource.Special` parameters _should_ be ignored, such as `CancellationToken`. API Versioning defines the `ApiVersion` as `BindingSource.Special` because the value can typically come from one or more places (even zero is technically possible). The value of `ApiVersion` is set and retrieved through `IApiVersioningFeature`.
This behavior is a deviation from `DefaultApiDescriptionProvider`, which considers and creates an `ApiParameterDescription` for route parameters, even if they don't have a formal `ParameterInfo` partner (as seen [here](../blob/release/6.0/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs#L233)).
Declaring the formal parameter `ApiVersion version` will not work because `TryParse` is delegated to `IApiVersionParser` so that a developer can opt to change the behavior if they want to. Parsing the value at this point in the pipeline is also too late and affects routing or would result in parsing the value more than once.
Ultimately, `EndpointMetadataApiDescriptionProvider` should create an `ApiParameterDescription` for every route parameter in the `RoutePattern` (e.g. template); regardless of whether there is a corresponding method parameter.
>It should be noted that this behavior does **not** affect routing and it only happens when a developer elects to version solely by URL segment.
### Expected Behavior
`EndpointMetadataApiDescriptionProvider` should have parity with `DefaultApiDescriptionProvider` wherever possible (obviously modeling binding isn't available for _Minimal APIs_).
Consider the following:
```c#
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddApiVersioning(options => options.ApiVersionReader = new UrlSegmentApiVersionReader())
.AddApiExplorer(options => options.options.SubstituteApiVersionInUrl = true);
app.MapGet( "v{version:apiVersion}/weatherforecast/{city}", () =>
{
return Enumerable.Range( 1, 5 ).Select( index =>
new WeatherForecast
(
DateTime.Now.AddDays( index ),
Random.Shared.Next( -20, 55 ),
summaries[Random.Shared.Next( summaries.Length )]
) );
} )
.WithApiVersionSet(app.NewApiVersionSet().HasApiVersion(1.0).Build());
```
This should produce `ApiDescription.RelativePath` with `v1/weatherforecast/{city}`, but instead produces `v{version}/weatherforecast/{city}`. Worse still, there is no `ApiParameterDescription` for `{version}`. API Versioning relies on the existence of the parameter description and substitutes the token in the template with the corresponding API version value.
This behavior can be reproduced without API Versioning from a pure API Explorer perspective, but the API will not be reachable since the route parameter wouldn't be matched.
### Steps To Reproduce
_No response_
### Exceptions (if any)
_No response_
### .NET Version
6.0.300
### Anything else?
This was first reported in dotnet/aspnet-api-versioning#830.
ASP.NET Core 6.0
```
.NET SDK (reflecting any global.json):
Version: 6.0.300
Commit: 8473146e7d
Runtime Environment:
OS Name: Windows
OS Version: 10.0.22000
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\6.0.300\
Host (useful for support):
Version: 6.0.5
Commit: 70ae3df4a6
.NET SDKs installed:
6.0.202 [C:\Program Files\dotnet\sdk]
6.0.203 [C:\Program Files\dotnet\sdk]
6.0.300 [C:\Program Files\dotnet\sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 3.1.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 3.1.25 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
```
Contributor guide
Assessment
This issue has not been assessed yet.