66using System . Diagnostics ;
77using System . Linq ;
88using System . Reflection ;
9+ using System . Runtime . CompilerServices ;
910using System . Threading ;
1011using Microsoft . AspNetCore . Http ;
1112using Microsoft . AspNetCore . Http . Metadata ;
@@ -21,7 +22,6 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
2122{
2223 internal class EndpointMetadataApiDescriptionProvider : IApiDescriptionProvider
2324 {
24- // IApiResponseMetadataProvider,
2525 private readonly EndpointDataSource _endpointDataSource ;
2626
2727 // Executes before MVC's DefaultApiDescriptionProvider and GrpcHttpApiDescriptionProvider for no particular reason :D
@@ -57,6 +57,21 @@ public void OnProvidersExecuted(ApiDescriptionProviderContext context)
5757
5858 private static ApiDescription CreateApiDescription ( RouteEndpoint routeEndpoint , string httpMethod , MethodInfo methodInfo )
5959 {
60+ // Swagger uses the "controller" name to group endpoints together.
61+ // For now, put all methods defined the same declaring type together.
62+ string controllerName ;
63+
64+ if ( methodInfo . DeclaringType is not null && ! IsCompilerGenerated ( methodInfo . DeclaringType ) )
65+ {
66+ controllerName = methodInfo . DeclaringType . Name ;
67+ }
68+ else
69+ {
70+ // If the declaring type is null or compiler-generated (e.g. lambdas),
71+ // group the methods under a "Map" controller.
72+ controllerName = "Map" ;
73+ }
74+
6075 var apiDescription = new ApiDescription
6176 {
6277 HttpMethod = httpMethod ,
@@ -65,10 +80,7 @@ private static ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint,
6580 {
6681 RouteValues =
6782 {
68- // Swagger uses this to group endpoints together.
69- // For now, put all endpoints configured with Map(Delegate) together.
70- // TODO: Use some other metadata for this.
71- [ "controller" ] = "Map" ,
83+ [ "controller" ] = controllerName ,
7284 } ,
7385 } ,
7486 } ;
@@ -288,5 +300,11 @@ private static void AddResponseContentTypes(IList<ApiResponseFormat> apiResponse
288300 } ) ;
289301 }
290302 }
303+
304+ // The CompilerGeneratedAttribute doesn't always get added so we also check if the type name starts with "<"
305+ // For example,w "<>c" is a "declaring" type the C# compiler will generate without the attribute for a top-level lambda
306+ // REVIEW: Is there a better way to do this?
307+ private static bool IsCompilerGenerated ( Type type ) =>
308+ Attribute . IsDefined ( type , typeof ( CompilerGeneratedAttribute ) ) || type . Name . StartsWith ( '<' ) ;
291309 }
292310}
0 commit comments