From f3aebf15c267652a75917487fac68fb18c3309d3 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Mon, 11 Oct 2021 23:41:35 +0000 Subject: [PATCH 1/5] Support setting ApiParameterRoutInfo for endpoints --- .../Routing/src/Properties/AssemblyInfo.cs | 1 + .../EndpointMetadataApiDescriptionProvider.cs | 61 ++++++++++++++-- ...pointMetadataApiDescriptionProviderTest.cs | 70 +++++++++++++++++++ 3 files changed, 128 insertions(+), 4 deletions(-) diff --git a/src/Http/Routing/src/Properties/AssemblyInfo.cs b/src/Http/Routing/src/Properties/AssemblyInfo.cs index 4d9f8603345f..93c67c32e9c9 100644 --- a/src/Http/Routing/src/Properties/AssemblyInfo.cs +++ b/src/Http/Routing/src/Properties/AssemblyInfo.cs @@ -5,4 +5,5 @@ [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Routing.Microbenchmarks, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Routing.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.ApiExplorer.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs index 29413558b121..63228cc1b06c 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -25,23 +25,31 @@ internal class EndpointMetadataApiDescriptionProvider : IApiDescriptionProvider private readonly IHostEnvironment _environment; private readonly IServiceProviderIsService? _serviceProviderIsService; private readonly ParameterBindingMethodCache ParameterBindingMethodCache = new(); + private readonly ParameterPolicyFactory? _parameterPolicyFactory; // Executes before MVC's DefaultApiDescriptionProvider and GrpcHttpApiDescriptionProvider for no particular reason. public int Order => -1100; - public EndpointMetadataApiDescriptionProvider(EndpointDataSource endpointDataSource, IHostEnvironment environment) - : this(endpointDataSource, environment, null) + public EndpointMetadataApiDescriptionProvider(EndpointDataSource endpointDataSource, IHostEnvironment environment, ParameterPolicyFactory parameterPolicyFactory) + :this(endpointDataSource, environment, null, parameterPolicyFactory) + { + } + + public EndpointMetadataApiDescriptionProvider(EndpointDataSource endpointDataSource, IHostEnvironment environment, IServiceProviderIsService serviceProviderIsService) + :this(endpointDataSource, environment, serviceProviderIsService, null) { } public EndpointMetadataApiDescriptionProvider( EndpointDataSource endpointDataSource, IHostEnvironment environment, - IServiceProviderIsService? serviceProviderIsService) + IServiceProviderIsService? serviceProviderIsService, + ParameterPolicyFactory? parameterPolicyFactory) { _endpointDataSource = endpointDataSource; _environment = environment; _serviceProviderIsService = serviceProviderIsService; + _parameterPolicyFactory = parameterPolicyFactory; } public void OnProvidersExecuting(ApiDescriptionProviderContext context) @@ -161,6 +169,7 @@ private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, string var nullability = nullabilityContext.Create(parameter); var isOptional = parameter.HasDefaultValue || nullability.ReadState != NullabilityState.NotNull || allowEmpty; var parameterDescriptor = CreateParameterDescriptor(parameter); + var routeInfo = CreateParameterRouteInfo(pattern, parameter, isOptional); return new ApiParameterDescription { @@ -170,7 +179,8 @@ private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, string DefaultValue = parameter.DefaultValue, Type = parameter.ParameterType, IsRequired = !isOptional, - ParameterDescriptor = parameterDescriptor + ParameterDescriptor = parameterDescriptor, + RouteInfo = routeInfo }; } @@ -182,6 +192,49 @@ private static ParameterDescriptor CreateParameterDescriptor(ParameterInfo param ParameterType = parameter.ParameterType, }; + private ApiParameterRouteInfo? CreateParameterRouteInfo(RoutePattern pattern, ParameterInfo parameter, bool isOptional) + { + if (parameter.Name is null) + { + throw new InvalidOperationException($"Encountered a parameter of type '{parameter.ParameterType}' without a name. Parameters must have a name."); + } + + // Only produce a `RouteInfo` property for parameters that are defined in the route template + if (pattern.GetParameter(parameter.Name) is not RoutePatternParameterPart parameterPart) + { + return null; + } + + var constraints = new List(); + + if (pattern.ParameterPolicies.TryGetValue(parameter.Name, out var parameterPolicies)) + { + foreach (var parameterPolicy in parameterPolicies) + { + if (parameterPolicy.ParameterPolicy is IRouteConstraint constraint) + { + constraints.Add(constraint); + } + + if (parameterPolicy.Content is string constraintText) + { + var policy = _parameterPolicyFactory?.Create(parameterPart, constraintText); + if (policy is IRouteConstraint generatedConstraint) + { + constraints.Add(generatedConstraint); + } + } + } + } + + return new ApiParameterRouteInfo() + { + Constraints = constraints.AsEnumerable(), + DefaultValue = parameter.DefaultValue, + IsOptional = isOptional + }; + } + // TODO: Share more of this logic with RequestDelegateFactory.CreateArgument(...) using RequestDelegateFactoryUtilities // which is shared source. private (BindingSource, string, bool, Type) GetBindingSourceAndName(ParameterInfo parameter, RoutePattern pattern) diff --git a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs index c5686c5a24a8..7a80d8d3b497 100644 --- a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs +++ b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; @@ -860,6 +861,75 @@ public void HandleIAcceptsMetadataWithConsumesAttributeAndInferredOptionalFromBo #nullable restore + [Fact] + public void ProducesRouteInfoOnlyForRouteParameters() + { + var builder = CreateBuilder(); + string GetName(int fromQuery, string name = "default") => $"Hello {name}!"; + builder.MapGet("/api/todos/{name}", GetName); + var context = new ApiDescriptionProviderContext(Array.Empty()); + + var endpointDataSource = builder.DataSources.OfType().Single(); + var hostEnvironment = new HostEnvironment + { + ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) + }; + var provider = new EndpointMetadataApiDescriptionProvider( + endpointDataSource, + hostEnvironment, + new ServiceProviderIsService(), + new DefaultParameterPolicyFactory(Options.Create(new RouteOptions()), new TestServiceProvider())); + + // Act + provider.OnProvidersExecuting(context); + + // Assert + var apiDescription = Assert.Single(context.Results); + Assert.Collection(apiDescription.ParameterDescriptions, + parameter => { + Assert.Equal("fromQuery", parameter.Name); + Assert.Null(parameter.RouteInfo); + }, + parameter => { + Assert.Equal("name", parameter.Name); + Assert.NotNull(parameter.RouteInfo); + Assert.Empty(parameter.RouteInfo!.Constraints); + Assert.True(parameter.RouteInfo!.IsOptional); + Assert.Equal("default", parameter.RouteInfo!.DefaultValue); + }); + } + + [Fact] + public void HandlesEndpointWithRouteConstraints() + { + var builder = CreateBuilder(); + builder.MapGet("/api/todos/{name:minlength(8):guid:maxlength(20)}", (string name) => ""); + var context = new ApiDescriptionProviderContext(Array.Empty()); + + var endpointDataSource = builder.DataSources.OfType().Single(); + var hostEnvironment = new HostEnvironment + { + ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) + }; + var provider = new EndpointMetadataApiDescriptionProvider( + endpointDataSource, + hostEnvironment, + new ServiceProviderIsService(), + new DefaultParameterPolicyFactory(Options.Create(new RouteOptions()), new TestServiceProvider())); + + // Act + provider.OnProvidersExecuting(context); + + // Assert + var apiDescription = Assert.Single(context.Results); + var parameter = Assert.Single(apiDescription.ParameterDescriptions); + Assert.NotNull(parameter.RouteInfo); + Assert.Collection(parameter.RouteInfo!.Constraints, + constraint => Assert.IsType(constraint), + constraint => Assert.IsType(constraint), + constraint => Assert.IsType(constraint)); + } + private static IEnumerable GetSortedMediaTypes(ApiResponseType apiResponseType) { return apiResponseType.ApiResponseFormats From 38b17e2f68bcf473cd935d47bcb815ba34d86a59 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Mon, 18 Oct 2021 22:41:50 +0000 Subject: [PATCH 2/5] Address feedback from peer review --- .../EndpointMetadataApiDescriptionProvider.cs | 32 +++--------- ...pointMetadataApiDescriptionProviderTest.cs | 50 +++++++++---------- 2 files changed, 32 insertions(+), 50 deletions(-) diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs index 63228cc1b06c..424faad6e0db 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -30,21 +30,11 @@ internal class EndpointMetadataApiDescriptionProvider : IApiDescriptionProvider // Executes before MVC's DefaultApiDescriptionProvider and GrpcHttpApiDescriptionProvider for no particular reason. public int Order => -1100; - public EndpointMetadataApiDescriptionProvider(EndpointDataSource endpointDataSource, IHostEnvironment environment, ParameterPolicyFactory parameterPolicyFactory) - :this(endpointDataSource, environment, null, parameterPolicyFactory) - { - } - - public EndpointMetadataApiDescriptionProvider(EndpointDataSource endpointDataSource, IHostEnvironment environment, IServiceProviderIsService serviceProviderIsService) - :this(endpointDataSource, environment, serviceProviderIsService, null) - { - } - public EndpointMetadataApiDescriptionProvider( EndpointDataSource endpointDataSource, IHostEnvironment environment, - IServiceProviderIsService? serviceProviderIsService, - ParameterPolicyFactory? parameterPolicyFactory) + ParameterPolicyFactory parameterPolicyFactory, + IServiceProviderIsService? serviceProviderIsService) { _endpointDataSource = endpointDataSource; _environment = environment; @@ -207,22 +197,14 @@ private static ParameterDescriptor CreateParameterDescriptor(ParameterInfo param var constraints = new List(); - if (pattern.ParameterPolicies.TryGetValue(parameter.Name, out var parameterPolicies)) + if (pattern.ParameterPolicies.TryGetValue(parameter.Name, out var parameterPolicyReferences)) { - foreach (var parameterPolicy in parameterPolicies) + foreach (var parameterPolicyReference in parameterPolicyReferences) { - if (parameterPolicy.ParameterPolicy is IRouteConstraint constraint) - { - constraints.Add(constraint); - } - - if (parameterPolicy.Content is string constraintText) + var policy = _parameterPolicyFactory?.Create(parameterPart, parameterPolicyReference); + if (policy is IRouteConstraint generatedConstraint) { - var policy = _parameterPolicyFactory?.Create(parameterPart, constraintText); - if (policy is IRouteConstraint generatedConstraint) - { - constraints.Add(generatedConstraint); - } + constraints.Add(generatedConstraint); } } } diff --git a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs index 7a80d8d3b497..28f6677eb973 100644 --- a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs +++ b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs @@ -503,7 +503,7 @@ public void RespectsProducesProblemExtensionMethod() { ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) }; - var provider = new EndpointMetadataApiDescriptionProvider(endpointDataSource, hostEnvironment, new ServiceProviderIsService()); + var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource); // Act provider.OnProvidersExecuting(context); @@ -528,7 +528,7 @@ public void RespectsProducesWithGroupNameExtensionMethod() { ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) }; - var provider = new EndpointMetadataApiDescriptionProvider(endpointDataSource, hostEnvironment, new ServiceProviderIsService()); + var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource); // Act provider.OnProvidersExecuting(context); @@ -553,7 +553,7 @@ public void RespectsExcludeFromDescription() { ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) }; - var provider = new EndpointMetadataApiDescriptionProvider(endpointDataSource, hostEnvironment, new ServiceProviderIsService()); + var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource); // Act provider.OnProvidersExecuting(context); @@ -579,7 +579,7 @@ public void HandlesProducesWithProducesProblem() { ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) }; - var provider = new EndpointMetadataApiDescriptionProvider(endpointDataSource, hostEnvironment, new ServiceProviderIsService()); + var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource); // Act provider.OnProvidersExecuting(context); @@ -630,7 +630,7 @@ public void HandleMultipleProduces() { ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) }; - var provider = new EndpointMetadataApiDescriptionProvider(endpointDataSource, hostEnvironment, new ServiceProviderIsService()); + var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource); // Act provider.OnProvidersExecuting(context); @@ -668,7 +668,7 @@ public void HandleAcceptsMetadata() { ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) }; - var provider = new EndpointMetadataApiDescriptionProvider(endpointDataSource, hostEnvironment, new ServiceProviderIsService()); + var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource); // Act provider.OnProvidersExecuting(context); @@ -701,7 +701,7 @@ public void HandleAcceptsMetadataWithTypeParameter() { ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) }; - var provider = new EndpointMetadataApiDescriptionProvider(endpointDataSource, hostEnvironment, new ServiceProviderIsService()); + var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource); // Act provider.OnProvidersExecuting(context); @@ -729,7 +729,7 @@ public void FavorsProducesMetadataOverAttribute() { ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) }; - var provider = new EndpointMetadataApiDescriptionProvider(endpointDataSource, hostEnvironment, new ServiceProviderIsService()); + var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource); // Act provider.OnProvidersExecuting(context); @@ -764,7 +764,7 @@ public void HandleDefaultIAcceptsMetadataForRequiredBodyParameter() { ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) }; - var provider = new EndpointMetadataApiDescriptionProvider(endpointDataSource, hostEnvironment, new ServiceProviderIsService()); + var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource); // Act provider.OnProvidersExecuting(context); @@ -802,7 +802,7 @@ public void HandleDefaultIAcceptsMetadataForOptionalBodyParameter() { ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) }; - var provider = new EndpointMetadataApiDescriptionProvider(endpointDataSource, hostEnvironment, new ServiceProviderIsService()); + var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource); // Act provider.OnProvidersExecuting(context); @@ -840,7 +840,7 @@ public void HandleIAcceptsMetadataWithConsumesAttributeAndInferredOptionalFromBo { ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) }; - var provider = new EndpointMetadataApiDescriptionProvider(endpointDataSource, hostEnvironment, new ServiceProviderIsService()); + var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource); // Act provider.OnProvidersExecuting(context); @@ -877,8 +877,8 @@ public void ProducesRouteInfoOnlyForRouteParameters() var provider = new EndpointMetadataApiDescriptionProvider( endpointDataSource, hostEnvironment, - new ServiceProviderIsService(), - new DefaultParameterPolicyFactory(Options.Create(new RouteOptions()), new TestServiceProvider())); + new DefaultParameterPolicyFactory(Options.Create(new RouteOptions()), new TestServiceProvider()), + new ServiceProviderIsService()); // Act provider.OnProvidersExecuting(context); @@ -886,11 +886,13 @@ public void ProducesRouteInfoOnlyForRouteParameters() // Assert var apiDescription = Assert.Single(context.Results); Assert.Collection(apiDescription.ParameterDescriptions, - parameter => { + parameter => + { Assert.Equal("fromQuery", parameter.Name); Assert.Null(parameter.RouteInfo); }, - parameter => { + parameter => + { Assert.Equal("name", parameter.Name); Assert.NotNull(parameter.RouteInfo); Assert.Empty(parameter.RouteInfo!.Constraints); @@ -911,11 +913,7 @@ public void HandlesEndpointWithRouteConstraints() { ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) }; - var provider = new EndpointMetadataApiDescriptionProvider( - endpointDataSource, - hostEnvironment, - new ServiceProviderIsService(), - new DefaultParameterPolicyFactory(Options.Create(new RouteOptions()), new TestServiceProvider())); + var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource); // Act provider.OnProvidersExecuting(context); @@ -954,12 +952,8 @@ private static IList GetApiDescriptions( var endpoint = new RouteEndpoint(httpContext => Task.CompletedTask, routePattern, 0, endpointMetadata, displayName); var endpointDataSource = new DefaultEndpointDataSource(endpoint); - var hostEnvironment = new HostEnvironment - { - ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) - }; - var provider = new EndpointMetadataApiDescriptionProvider(endpointDataSource, hostEnvironment, new ServiceProviderIsService()); + var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource); provider.OnProvidersExecuting(context); provider.OnProvidersExecuted(context); @@ -967,6 +961,12 @@ private static IList GetApiDescriptions( return context.Results; } + private static EndpointMetadataApiDescriptionProvider CreateEndpointMetadataApiDescriptionProvider(EndpointDataSource endpointDataSource) => new EndpointMetadataApiDescriptionProvider( + endpointDataSource, + new HostEnvironment { ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) }, + new DefaultParameterPolicyFactory(Options.Create(new RouteOptions()), new TestServiceProvider()), + new ServiceProviderIsService()); + private static TestEndpointRouteBuilder CreateBuilder() => new TestEndpointRouteBuilder(new ApplicationBuilder(new TestServiceProvider())); From 43fd3cd5b30a0b3ccff47a3b0c4ffc233647429a Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Mon, 18 Oct 2021 22:49:11 +0000 Subject: [PATCH 3/5] Make constraints list read-only --- .../src/EndpointMetadataApiDescriptionProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs index 424faad6e0db..4048cf4d79bb 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -211,7 +211,7 @@ private static ParameterDescriptor CreateParameterDescriptor(ParameterInfo param return new ApiParameterRouteInfo() { - Constraints = constraints.AsEnumerable(), + Constraints = constraints.AsReadOnly(), DefaultValue = parameter.DefaultValue, IsOptional = isOptional }; From 6f6bc2b88759a08651589437d2925c34f46ed212 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Wed, 20 Oct 2021 19:59:38 +0000 Subject: [PATCH 4/5] Address more feedback from peer review --- src/Http/Http.Extensions/src/RequestDelegateFactory.cs | 2 +- .../src/EndpointMetadataApiDescriptionProvider.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index f9583b735f69..227be2f5769a 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -208,7 +208,7 @@ private static Expression CreateArgument(ParameterInfo parameter, FactoryContext { if (parameter.Name is null) { - throw new InvalidOperationException("A parameter does not have a name! Was it generated? All parameters must be named."); + throw new InvalidOperationException($"Encountered a parameter of type '{parameter.ParameterType}' without a name. Parameters must have a name."); } var parameterCustomAttributes = parameter.GetCustomAttributes(); diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs index 4048cf4d79bb..3f7be70aec9f 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -25,7 +25,7 @@ internal class EndpointMetadataApiDescriptionProvider : IApiDescriptionProvider private readonly IHostEnvironment _environment; private readonly IServiceProviderIsService? _serviceProviderIsService; private readonly ParameterBindingMethodCache ParameterBindingMethodCache = new(); - private readonly ParameterPolicyFactory? _parameterPolicyFactory; + private readonly ParameterPolicyFactory _parameterPolicyFactory; // Executes before MVC's DefaultApiDescriptionProvider and GrpcHttpApiDescriptionProvider for no particular reason. public int Order => -1100; @@ -201,7 +201,7 @@ private static ParameterDescriptor CreateParameterDescriptor(ParameterInfo param { foreach (var parameterPolicyReference in parameterPolicyReferences) { - var policy = _parameterPolicyFactory?.Create(parameterPart, parameterPolicyReference); + var policy = _parameterPolicyFactory.Create(parameterPart, parameterPolicyReference); if (policy is IRouteConstraint generatedConstraint) { constraints.Add(generatedConstraint); From df025b73ab033e625210e545751f0cf3059a5a88 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Wed, 20 Oct 2021 22:45:41 +0000 Subject: [PATCH 5/5] Fix up test --- src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs index 3474a2b4794e..c3c9ec733ebf 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs @@ -904,7 +904,7 @@ public void CreateThrowsInvalidOperationExceptionGivenUnnamedArgument() var unnamedParameter = Expression.Parameter(typeof(int)); var lambda = Expression.Lambda(Expression.Block(), unnamedParameter); var ex = Assert.Throws(() => RequestDelegateFactory.Create(lambda.Compile())); - Assert.Equal("A parameter does not have a name! Was it generated? All parameters must be named.", ex.Message); + Assert.Equal("Encountered a parameter of type 'System.Runtime.CompilerServices.Closure' without a name. Parameters must have a name.", ex.Message); } [Fact]