Skip to content

EndpointMetadataApiDescriptionProvider Misses Route Parameters #41773

Open
@commonsensesoftware

Description

@commonsensesoftware

Is there an existing issue for this?

  • 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, 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).

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:

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]

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcbugThis issue describes a behavior which is not expected - a bug.feature-openapi

    Type

    No type

    Projects

    Status

    8.0.x

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions