Skip to content

Commit 113f6b2

Browse files
Fix discovery of endpoint metadata from async route handlers (#41388)
Fixes #41384 Co-authored-by: Damian Edwards <[email protected]>
1 parent bd6dbc1 commit 113f6b2

File tree

2 files changed

+104
-2
lines changed

2 files changed

+104
-2
lines changed

src/Http/Http.Extensions/src/RequestDelegateFactory.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,13 +406,19 @@ private static void AddTypeProvidedMetadata(MethodInfo methodInfo, List<object>
406406
}
407407

408408
// Get metadata from return type
409-
if (methodInfo.ReturnType is not null && typeof(IEndpointMetadataProvider).IsAssignableFrom(methodInfo.ReturnType))
409+
var returnType = methodInfo.ReturnType;
410+
if (AwaitableInfo.IsTypeAwaitable(returnType, out var awaitableInfo))
411+
{
412+
returnType = awaitableInfo.ResultType;
413+
}
414+
415+
if (returnType is not null && typeof(IEndpointMetadataProvider).IsAssignableFrom(returnType))
410416
{
411417
// Return type implements IEndpointMetadataProvider
412418
var context = new EndpointMetadataContext(methodInfo, metadata, services);
413419
invokeArgs ??= new object[1];
414420
invokeArgs[0] = context;
415-
PopulateMetadataForEndpointMethod.MakeGenericMethod(methodInfo.ReturnType).Invoke(null, invokeArgs);
421+
PopulateMetadataForEndpointMethod.MakeGenericMethod(returnType).Invoke(null, invokeArgs);
416422
}
417423
}
418424

src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5081,6 +5081,32 @@ public void Create_DiscoversEndpointMetadata_FromReturnTypeImplementingIEndpoint
50815081
Assert.Contains(result.EndpointMetadata, m => m is CustomEndpointMetadata { Source: MetadataSource.ReturnType });
50825082
}
50835083

5084+
[Fact]
5085+
public void Create_DiscoversEndpointMetadata_FromTaskWrappedReturnTypeImplementingIEndpointMetadataProvider()
5086+
{
5087+
// Arrange
5088+
var @delegate = () => Task.FromResult(new AddsCustomEndpointMetadataResult());
5089+
5090+
// Act
5091+
var result = RequestDelegateFactory.Create(@delegate);
5092+
5093+
// Assert
5094+
Assert.Contains(result.EndpointMetadata, m => m is CustomEndpointMetadata { Source: MetadataSource.ReturnType });
5095+
}
5096+
5097+
[Fact]
5098+
public void Create_DiscoversEndpointMetadata_FromValueTaskWrappedReturnTypeImplementingIEndpointMetadataProvider()
5099+
{
5100+
// Arrange
5101+
var @delegate = () => ValueTask.FromResult(new AddsCustomEndpointMetadataResult());
5102+
5103+
// Act
5104+
var result = RequestDelegateFactory.Create(@delegate);
5105+
5106+
// Assert
5107+
Assert.Contains(result.EndpointMetadata, m => m is CustomEndpointMetadata { Source: MetadataSource.ReturnType });
5108+
}
5109+
50845110
[Fact]
50855111
public void Create_CombinesDefaultMetadata_AndMetadataFromReturnTypesImplementingIEndpointMetadataProvider()
50865112
{
@@ -5103,6 +5129,50 @@ public void Create_CombinesDefaultMetadata_AndMetadataFromReturnTypesImplementin
51035129
Assert.Contains(result.EndpointMetadata, m => m is DefaultMetadataCountMetadata { Count: 2 });
51045130
}
51055131

5132+
[Fact]
5133+
public void Create_CombinesDefaultMetadata_AndMetadataFromTaskWrappedReturnTypesImplementingIEndpointMetadataProvider()
5134+
{
5135+
// Arrange
5136+
var @delegate = () => Task.FromResult(new CountsDefaultEndpointMetadataResult());
5137+
var options = new RequestDelegateFactoryOptions
5138+
{
5139+
InitialEndpointMetadata = new List<object>
5140+
{
5141+
new CustomEndpointMetadata { Source = MetadataSource.Caller }
5142+
}
5143+
};
5144+
5145+
// Act
5146+
var result = RequestDelegateFactory.Create(@delegate, options);
5147+
5148+
// Assert
5149+
Assert.Contains(result.EndpointMetadata, m => m is CustomEndpointMetadata { Source: MetadataSource.Caller });
5150+
// Expecting '2' as only MethodInfo and initial metadata will be in the metadata list when this metadata item is added
5151+
Assert.Contains(result.EndpointMetadata, m => m is DefaultMetadataCountMetadata { Count: 2 });
5152+
}
5153+
5154+
[Fact]
5155+
public void Create_CombinesDefaultMetadata_AndMetadataFromValueTaskWrappedReturnTypesImplementingIEndpointMetadataProvider()
5156+
{
5157+
// Arrange
5158+
var @delegate = () => ValueTask.FromResult(new CountsDefaultEndpointMetadataResult());
5159+
var options = new RequestDelegateFactoryOptions
5160+
{
5161+
InitialEndpointMetadata = new List<object>
5162+
{
5163+
new CustomEndpointMetadata { Source = MetadataSource.Caller }
5164+
}
5165+
};
5166+
5167+
// Act
5168+
var result = RequestDelegateFactory.Create(@delegate, options);
5169+
5170+
// Assert
5171+
Assert.Contains(result.EndpointMetadata, m => m is CustomEndpointMetadata { Source: MetadataSource.Caller });
5172+
// Expecting '2' as only MethodInfo and initial metadata will be in the metadata list when this metadata item is added
5173+
Assert.Contains(result.EndpointMetadata, m => m is DefaultMetadataCountMetadata { Count: 2 });
5174+
}
5175+
51065176
[Fact]
51075177
public void Create_CombinesDefaultMetadata_AndMetadataFromParameterTypesImplementingIEndpointParameterMetadataProvider()
51085178
{
@@ -5194,6 +5264,32 @@ public void Create_AllowsRemovalOfDefaultMetadata_ByReturnTypesImplementingIEndp
51945264
Assert.DoesNotContain(result.EndpointMetadata, m => m is IAcceptsMetadata);
51955265
}
51965266

5267+
[Fact]
5268+
public void Create_AllowsRemovalOfDefaultMetadata_ByTaskWrappedReturnTypesImplementingIEndpointMetadataProvider()
5269+
{
5270+
// Arrange
5271+
var @delegate = (Todo todo) => Task.FromResult(new RemovesAcceptsMetadataResult());
5272+
5273+
// Act
5274+
var result = RequestDelegateFactory.Create(@delegate);
5275+
5276+
// Assert
5277+
Assert.DoesNotContain(result.EndpointMetadata, m => m is IAcceptsMetadata);
5278+
}
5279+
5280+
[Fact]
5281+
public void Create_AllowsRemovalOfDefaultMetadata_ByValueTaskWrappedReturnTypesImplementingIEndpointMetadataProvider()
5282+
{
5283+
// Arrange
5284+
var @delegate = (Todo todo) => ValueTask.FromResult(new RemovesAcceptsMetadataResult());
5285+
5286+
// Act
5287+
var result = RequestDelegateFactory.Create(@delegate);
5288+
5289+
// Assert
5290+
Assert.DoesNotContain(result.EndpointMetadata, m => m is IAcceptsMetadata);
5291+
}
5292+
51975293
[Fact]
51985294
public void Create_AllowsRemovalOfDefaultMetadata_ByParameterTypesImplementingIEndpointParameterMetadataProvider()
51995295
{

0 commit comments

Comments
 (0)