33
44using System . Linq ;
55using System . Reflection ;
6+ using Microsoft . AspNetCore . Builder ;
67using Microsoft . AspNetCore . Http ;
8+ using Microsoft . AspNetCore . Http . Metadata ;
79using Microsoft . AspNetCore . Mvc . ActionConstraints ;
810using Microsoft . AspNetCore . Mvc . ApiExplorer ;
911using Microsoft . AspNetCore . Mvc . Filters ;
@@ -19,6 +21,8 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels;
1921internal class DefaultApplicationModelProvider : IApplicationModelProvider
2022#pragma warning restore CA1852 // Seal internal types
2123{
24+ private static readonly MethodInfo PopulateMetadataForEndpointMethod = typeof ( EndpointMetadataPopulator ) . GetMethod ( nameof ( PopulateMetadataForEndpoint ) , BindingFlags . NonPublic | BindingFlags . Static ) ! ;
25+
2226 private readonly MvcOptions _mvcOptions ;
2327 private readonly IModelMetadataProvider _modelMetadataProvider ;
2428 private readonly Func < ActionContext , bool > _supportsAllRequests ;
@@ -349,9 +353,33 @@ internal PropertyModel CreatePropertyModel(PropertyInfo propertyInfo)
349353 applicableAttributes . AddRange ( routeAttributes ) ;
350354 AddRange ( actionModel . Selectors , CreateSelectors ( applicableAttributes ) ) ;
351355
356+ // There may be additional metadata for the action that is associated with the return type.
357+ AddEndpointMetadata ( actionModel , methodInfo ) ;
358+
352359 return actionModel ;
353360 }
354361
362+ private static void AddEndpointMetadata ( ActionModel actionModel , MethodInfo methodInfo )
363+ {
364+ // Get metadata from return type
365+ var returnType = methodInfo . ReturnType ;
366+ if ( CoercedAwaitableInfo . IsTypeAwaitable ( returnType , out var coercedAwaitableInfo ) )
367+ {
368+ returnType = coercedAwaitableInfo . AwaitableInfo . ResultType ;
369+ }
370+
371+ if ( returnType is not null && typeof ( IEndpointMetadataProvider ) . IsAssignableFrom ( returnType ) )
372+ {
373+ // Return type implements IEndpointMetadataProvider
374+ var builder = new InertEndpointBuilder ( ) ;
375+ var invokeArgs = new object [ 2 ] ;
376+ invokeArgs [ 0 ] = methodInfo ;
377+ invokeArgs [ 1 ] = builder ;
378+ PopulateMetadataForEndpointMethod . MakeGenericMethod ( returnType ) . Invoke ( null , invokeArgs ) ;
379+ actionModel . Properties . Add ( typeof ( ProducesResponseTypeAttribute ) , builder . Metadata . ToArray ( ) ) ;
380+ }
381+ }
382+
355383 private string CanonicalizeActionName ( string actionName )
356384 {
357385 const string Suffix = "Async" ;
@@ -675,4 +703,18 @@ private static void AddRange<T>(IList<T> list, IEnumerable<T> items)
675703 list . Add ( item ) ;
676704 }
677705 }
706+
707+ private static void PopulateMetadataForEndpoint < T > ( MethodInfo method , EndpointBuilder builder )
708+ where T : IEndpointMetadataProvider
709+ {
710+ T . PopulateMetadata ( method , builder ) ;
711+ }
712+
713+ private sealed class InertEndpointBuilder : EndpointBuilder
714+ {
715+ public override Endpoint Build ( )
716+ {
717+ return new Endpoint ( RequestDelegate , new EndpointMetadataCollection ( Metadata ) , DisplayName ) ;
718+ }
719+ }
678720}
0 commit comments