From 2032c68402a8cae2bc9e312ddbedc6fa47878b97 Mon Sep 17 00:00:00 2001 From: martincostello Date: Sat, 3 Jul 2021 11:54:39 +0100 Subject: [PATCH 1/2] Populate ActionDescriptor EndpointMetadata Populate the EndpointMetadata property of the ActionDescriptor on ActionDescriptor instances for minimal actions so that the attributes associated with the request delegate can be inspected. Relates to #34061. --- .../src/Routing/EndpointMetadataCollection.cs | 2 ++ .../EndpointMetadataApiDescriptionProvider.cs | 27 +++++++++++++++++++ ...pointMetadataApiDescriptionProviderTest.cs | 18 ++++++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/Http/Http.Abstractions/src/Routing/EndpointMetadataCollection.cs b/src/Http/Http.Abstractions/src/Routing/EndpointMetadataCollection.cs index 6051068e527c..5aa8f85a88da 100644 --- a/src/Http/Http.Abstractions/src/Routing/EndpointMetadataCollection.cs +++ b/src/Http/Http.Abstractions/src/Routing/EndpointMetadataCollection.cs @@ -151,8 +151,10 @@ private T[] GetOrderedMetadataSlow() where T : class /// public struct Enumerator : IEnumerator { +#pragma warning disable IDE0044 // Intentionally not readonly to prevent defensive struct copies private object[] _items; +#pragma warning restore IDE0044 private int _index; internal Enumerator(EndpointMetadataCollection collection) diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs index 9623cb3f9c19..49bf8897426c 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -121,6 +121,8 @@ private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, string AddSupportedRequestFormats(apiDescription.SupportedRequestFormats, hasJsonBody, routeEndpoint.Metadata); AddSupportedResponseTypes(apiDescription.SupportedResponseTypes, methodInfo.ReturnType, routeEndpoint.Metadata); + AddActionDescriptorEndpointMetadata(apiDescription.ActionDescriptor, routeEndpoint.Metadata); + return apiDescription; } @@ -335,6 +337,31 @@ private static void AddResponseContentTypes(IList apiResponse } } + private static void AddActionDescriptorEndpointMetadata( + ActionDescriptor actionDescriptor, + EndpointMetadataCollection endpointMetadata) + { + if (endpointMetadata.Count > 0) + { + // ActionDescriptor.EndpointMetadata is an empty array by + // default so need to add the metadata into a new list. + var metadata = new List(endpointMetadata.Count); + + foreach (var item in endpointMetadata) + { + if (item is not null) + { + metadata.Add(item); + } + } + + if (metadata.Count > 0) + { + actionDescriptor.EndpointMetadata = metadata; + } + } + } + // The CompilerGeneratedAttribute doesn't always get added so we also check if the type name starts with "<" // For example,w "<>c" is a "declaring" type the C# compiler will generate without the attribute for a top-level lambda // REVIEW: Is there a better way to do this? diff --git a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs index 231c2f3b20cd..c47ec8a158d9 100644 --- a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs +++ b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -310,6 +311,21 @@ public void AddsDisplayNameFromRouteEndpoint() Assert.Equal("FOO", apiDescription.ActionDescriptor.DisplayName); } + [Fact] + public void AddsMetadataFromRouteEndpoint() + { + var apiDescription = GetApiDescription([ApiExplorerSettings(IgnoreApi = true)]() => { }); + + Assert.NotEmpty(apiDescription.ActionDescriptor.EndpointMetadata); + + var apiExplorerSettings = apiDescription.ActionDescriptor.EndpointMetadata + .OfType() + .FirstOrDefault(); + + Assert.NotNull(apiExplorerSettings); + Assert.True(apiExplorerSettings.IgnoreApi); + } + private IList GetApiDescriptions( Delegate action, string pattern = null, @@ -367,7 +383,7 @@ private class ServiceProviderIsService : IServiceProviderIsService { public bool IsService(Type serviceType) => serviceType == typeof(IInferredServiceInterface); } - + private class HostEnvironment : IHostEnvironment { public string EnvironmentName { get; set; } From c85b905ed149fba8f01f262cc4316c73b0cef4f0 Mon Sep 17 00:00:00 2001 From: martincostello Date: Thu, 15 Jul 2021 07:08:41 +0100 Subject: [PATCH 2/2] Simplify metadata assignment Remove filtering and just directly populate the list. Fix typo in comment. --- .../EndpointMetadataApiDescriptionProvider.cs | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs index c7e7898ed774..2cd8275d8004 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -349,25 +349,12 @@ private static void AddActionDescriptorEndpointMetadata( { // ActionDescriptor.EndpointMetadata is an empty array by // default so need to add the metadata into a new list. - var metadata = new List(endpointMetadata.Count); - - foreach (var item in endpointMetadata) - { - if (item is not null) - { - metadata.Add(item); - } - } - - if (metadata.Count > 0) - { - actionDescriptor.EndpointMetadata = metadata; - } + actionDescriptor.EndpointMetadata = new List(endpointMetadata); } } // The CompilerGeneratedAttribute doesn't always get added so we also check if the type name starts with "<" - // For example,w "<>c" is a "declaring" type the C# compiler will generate without the attribute for a top-level lambda + // For example, "<>c" is a "declaring" type the C# compiler will generate without the attribute for a top-level lambda // REVIEW: Is there a better way to do this? private static bool IsCompilerGenerated(Type type) => Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute)) || type.Name.StartsWith('<');