6
6
using System . Diagnostics ;
7
7
using System . Linq ;
8
8
using System . Reflection ;
9
+ using System . Runtime . CompilerServices ;
9
10
using System . Threading ;
10
11
using Microsoft . AspNetCore . Http ;
11
12
using Microsoft . AspNetCore . Http . Metadata ;
@@ -21,7 +22,6 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
21
22
{
22
23
internal class EndpointMetadataApiDescriptionProvider : IApiDescriptionProvider
23
24
{
24
- // IApiResponseMetadataProvider,
25
25
private readonly EndpointDataSource _endpointDataSource ;
26
26
27
27
// Executes before MVC's DefaultApiDescriptionProvider and GrpcHttpApiDescriptionProvider for no particular reason :D
@@ -57,6 +57,21 @@ public void OnProvidersExecuted(ApiDescriptionProviderContext context)
57
57
58
58
private static ApiDescription CreateApiDescription ( RouteEndpoint routeEndpoint , string httpMethod , MethodInfo methodInfo )
59
59
{
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
+
60
75
var apiDescription = new ApiDescription
61
76
{
62
77
HttpMethod = httpMethod ,
@@ -65,10 +80,7 @@ private static ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint,
65
80
{
66
81
RouteValues =
67
82
{
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 ,
72
84
} ,
73
85
} ,
74
86
} ;
@@ -288,5 +300,11 @@ private static void AddResponseContentTypes(IList<ApiResponseFormat> apiResponse
288
300
} ) ;
289
301
}
290
302
}
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 ( '<' ) ;
291
309
}
292
310
}
0 commit comments