diff --git a/src/Http/Http.Abstractions/src/Routing/EndpointMetadataCollection.cs b/src/Http/Http.Abstractions/src/Routing/EndpointMetadataCollection.cs index f7a60d4043e4..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 readonly object[] _items; + 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 fe0a7bd1ec4a..2cd8275d8004 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -122,6 +122,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; } @@ -339,8 +341,20 @@ 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. + 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('<'); diff --git a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs index 30d4f0ea7c9a..e222305fd646 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.Security.Claims; using System.Threading; @@ -342,6 +343,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, @@ -399,7 +415,7 @@ private class ServiceProviderIsService : IServiceProviderIsService { public bool IsService(Type serviceType) => serviceType == typeof(IInferredServiceInterface); } - + private class HostEnvironment : IHostEnvironment { public string EnvironmentName { get; set; }