Skip to content

RequestDelegateFactory context classes ApplicationServices property #41952

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ Microsoft.AspNetCore.Http.IRouteHandlerFilter.InvokeAsync(Microsoft.AspNetCore.H
Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata
Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata.Name.get -> string?
Microsoft.AspNetCore.Http.RouteHandlerContext
Microsoft.AspNetCore.Http.RouteHandlerContext.ApplicationServices.get -> System.IServiceProvider!
Microsoft.AspNetCore.Http.RouteHandlerContext.EndpointMetadata.get -> Microsoft.AspNetCore.Http.EndpointMetadataCollection!
Microsoft.AspNetCore.Http.RouteHandlerContext.MethodInfo.get -> System.Reflection.MethodInfo!
Microsoft.AspNetCore.Http.RouteHandlerContext.RouteHandlerContext(System.Reflection.MethodInfo! methodInfo, Microsoft.AspNetCore.Http.EndpointMetadataCollection! endpointMetadata) -> void
Microsoft.AspNetCore.Http.RouteHandlerContext.RouteHandlerContext(System.Reflection.MethodInfo! methodInfo, Microsoft.AspNetCore.Http.EndpointMetadataCollection! endpointMetadata, System.IServiceProvider! applicationServices) -> void
Microsoft.AspNetCore.Http.RouteHandlerFilterDelegate
Microsoft.AspNetCore.Http.RouteHandlerInvocationContext
Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.RouteHandlerInvocationContext() -> void
Expand Down
13 changes: 12 additions & 1 deletion src/Http/Http.Abstractions/src/RouteHandlerContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@ public sealed class RouteHandlerContext
/// </summary>
/// <param name="methodInfo">The <see cref="MethodInfo"/> associated with the route handler of the current request.</param>
/// <param name="endpointMetadata">The <see cref="EndpointMetadataCollection"/> associated with the endpoint the filter is targeting.</param>
public RouteHandlerContext(MethodInfo methodInfo, EndpointMetadataCollection endpointMetadata)
/// <param name="applicationServices">The <see cref="IServiceProvider"/> instance used to access the application services.</param>
public RouteHandlerContext(MethodInfo methodInfo, EndpointMetadataCollection endpointMetadata, IServiceProvider applicationServices)
{
ArgumentNullException.ThrowIfNull(methodInfo);
ArgumentNullException.ThrowIfNull(endpointMetadata);
ArgumentNullException.ThrowIfNull(applicationServices);

MethodInfo = methodInfo;
EndpointMetadata = endpointMetadata;
ApplicationServices = applicationServices;
}

/// <summary>
Expand All @@ -31,4 +37,9 @@ public RouteHandlerContext(MethodInfo methodInfo, EndpointMetadataCollection end
/// The <see cref="EndpointMetadataCollection"/> associated with the current endpoint.
/// </summary>
public EndpointMetadataCollection EndpointMetadata { get; }

/// <summary>
/// Gets the <see cref="IServiceProvider"/> instance used to access application services.
/// </summary>
public IServiceProvider ApplicationServices { get; }
}
13 changes: 7 additions & 6 deletions src/Http/Http.Extensions/src/EndpointMetadataContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@ public sealed class EndpointMetadataContext
/// </summary>
/// <param name="method">The <see cref="MethodInfo"/> of the route handler delegate of the endpoint being created.</param>
/// <param name="endpointMetadata">The list of objects that will be added to the metadata of the endpoint.</param>
/// <param name="services">The <see cref="IServiceProvider"/> instance used to access application services.</param>
public EndpointMetadataContext(MethodInfo method, IList<object> endpointMetadata, IServiceProvider? services)
/// <param name="applicationServices">The <see cref="IServiceProvider"/> instance used to access application services.</param>
public EndpointMetadataContext(MethodInfo method, IList<object> endpointMetadata, IServiceProvider applicationServices)
{
ArgumentNullException.ThrowIfNull(method, nameof(method));
ArgumentNullException.ThrowIfNull(endpointMetadata, nameof(endpointMetadata));
ArgumentNullException.ThrowIfNull(method);
ArgumentNullException.ThrowIfNull(endpointMetadata);
ArgumentNullException.ThrowIfNull(applicationServices);

Method = method;
EndpointMetadata = endpointMetadata;
Services = services;
ApplicationServices = applicationServices;
}

/// <summary>
Expand All @@ -39,5 +40,5 @@ public EndpointMetadataContext(MethodInfo method, IList<object> endpointMetadata
/// <summary>
/// Gets the <see cref="IServiceProvider"/> instance used to access application services.
/// </summary>
public IServiceProvider? Services { get; }
public IServiceProvider ApplicationServices { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@ public sealed class EndpointParameterMetadataContext
/// </summary>
/// <param name="parameter">The parameter of the route handler delegate of the endpoint being created.</param>
/// <param name="endpointMetadata">The list of objects that will be added to the metadata of the endpoint.</param>
/// <param name="services">The <see cref="IServiceProvider"/> instance used to access application services.</param>
public EndpointParameterMetadataContext(ParameterInfo parameter, IList<object> endpointMetadata, IServiceProvider? services)
/// <param name="applicationServices">The <see cref="IServiceProvider"/> instance used to access application services.</param>
public EndpointParameterMetadataContext(ParameterInfo parameter, IList<object> endpointMetadata, IServiceProvider applicationServices)
{
ArgumentNullException.ThrowIfNull(parameter, nameof(parameter));
ArgumentNullException.ThrowIfNull(endpointMetadata, nameof(endpointMetadata));
ArgumentNullException.ThrowIfNull(parameter);
ArgumentNullException.ThrowIfNull(endpointMetadata);
ArgumentNullException.ThrowIfNull(applicationServices);

Parameter = parameter;
EndpointMetadata = endpointMetadata;
Services = services;
ApplicationServices = applicationServices;
}

/// <summary>
Expand All @@ -39,5 +40,5 @@ public EndpointParameterMetadataContext(ParameterInfo parameter, IList<object> e
/// <summary>
/// Gets the <see cref="IServiceProvider"/> instance used to access application services.
/// </summary>
public IServiceProvider? Services { get; }
public IServiceProvider ApplicationServices { get; }
}
8 changes: 4 additions & 4 deletions src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#nullable enable
Microsoft.AspNetCore.Http.Metadata.EndpointMetadataContext
Microsoft.AspNetCore.Http.Metadata.EndpointMetadataContext.EndpointMetadataContext(System.Reflection.MethodInfo! method, System.Collections.Generic.IList<object!>! endpointMetadata, System.IServiceProvider? services) -> void
Microsoft.AspNetCore.Http.Metadata.EndpointMetadataContext.ApplicationServices.get -> System.IServiceProvider!
Microsoft.AspNetCore.Http.Metadata.EndpointMetadataContext.EndpointMetadataContext(System.Reflection.MethodInfo! method, System.Collections.Generic.IList<object!>! endpointMetadata, System.IServiceProvider! applicationServices) -> void
Microsoft.AspNetCore.Http.Metadata.EndpointMetadataContext.EndpointMetadata.get -> System.Collections.Generic.IList<object!>!
Microsoft.AspNetCore.Http.Metadata.EndpointMetadataContext.Method.get -> System.Reflection.MethodInfo!
Microsoft.AspNetCore.Http.Metadata.EndpointMetadataContext.Services.get -> System.IServiceProvider?
Microsoft.AspNetCore.Http.Metadata.EndpointParameterMetadataContext
Microsoft.AspNetCore.Http.Metadata.EndpointParameterMetadataContext.EndpointParameterMetadataContext(System.Reflection.ParameterInfo! parameter, System.Collections.Generic.IList<object!>! endpointMetadata, System.IServiceProvider? services) -> void
Microsoft.AspNetCore.Http.Metadata.EndpointParameterMetadataContext.ApplicationServices.get -> System.IServiceProvider!
Microsoft.AspNetCore.Http.Metadata.EndpointParameterMetadataContext.EndpointParameterMetadataContext(System.Reflection.ParameterInfo! parameter, System.Collections.Generic.IList<object!>! endpointMetadata, System.IServiceProvider! applicationServices) -> void
Microsoft.AspNetCore.Http.Metadata.EndpointParameterMetadataContext.EndpointMetadata.get -> System.Collections.Generic.IList<object!>!
Microsoft.AspNetCore.Http.Metadata.EndpointParameterMetadataContext.Parameter.get -> System.Reflection.ParameterInfo!
Microsoft.AspNetCore.Http.Metadata.EndpointParameterMetadataContext.Services.get -> System.IServiceProvider?
Microsoft.AspNetCore.Http.Metadata.IEndpointMetadataProvider
Microsoft.AspNetCore.Http.Metadata.IEndpointMetadataProvider.PopulateMetadata(Microsoft.AspNetCore.Http.Metadata.EndpointMetadataContext! context) -> void
Microsoft.AspNetCore.Http.Metadata.IEndpointParameterMetadataProvider
Expand Down
16 changes: 12 additions & 4 deletions src/Http/Http.Extensions/src/RequestDelegateFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,8 @@ targetExpression is null
FilterContextExpr).Compile();
var routeHandlerContext = new RouteHandlerContext(
methodInfo,
new EndpointMetadataCollection(factoryContext.Metadata));
new EndpointMetadataCollection(factoryContext.Metadata),
factoryContext.ServiceProvider ?? EmptyServiceProvider.Instance);

for (var i = factoryContext.Filters.Count - 1; i >= 0; i--)
{
Expand Down Expand Up @@ -442,7 +443,7 @@ private static void AddTypeProvidedMetadata(MethodInfo methodInfo, List<object>
if (typeof(IEndpointParameterMetadataProvider).IsAssignableFrom(parameter.ParameterType))
{
// Parameter type implements IEndpointParameterMetadataProvider
var parameterContext = new EndpointParameterMetadataContext(parameter, metadata, services);
var parameterContext = new EndpointParameterMetadataContext(parameter, metadata, services ?? EmptyServiceProvider.Instance);
invokeArgs ??= new object[1];
invokeArgs[0] = parameterContext;
PopulateMetadataForParameterMethod.MakeGenericMethod(parameter.ParameterType).Invoke(null, invokeArgs);
Expand All @@ -451,7 +452,7 @@ private static void AddTypeProvidedMetadata(MethodInfo methodInfo, List<object>
if (typeof(IEndpointMetadataProvider).IsAssignableFrom(parameter.ParameterType))
{
// Parameter type implements IEndpointMetadataProvider
var context = new EndpointMetadataContext(methodInfo, metadata, services);
var context = new EndpointMetadataContext(methodInfo, metadata, services ?? EmptyServiceProvider.Instance);
invokeArgs ??= new object[1];
invokeArgs[0] = context;
PopulateMetadataForEndpointMethod.MakeGenericMethod(parameter.ParameterType).Invoke(null, invokeArgs);
Expand All @@ -468,7 +469,7 @@ private static void AddTypeProvidedMetadata(MethodInfo methodInfo, List<object>
if (returnType is not null && typeof(IEndpointMetadataProvider).IsAssignableFrom(returnType))
{
// Return type implements IEndpointMetadataProvider
var context = new EndpointMetadataContext(methodInfo, metadata, services);
var context = new EndpointMetadataContext(methodInfo, metadata, services ?? EmptyServiceProvider.Instance);
invokeArgs ??= new object[1];
invokeArgs[0] = context;
PopulateMetadataForEndpointMethod.MakeGenericMethod(returnType).Invoke(null, invokeArgs);
Expand Down Expand Up @@ -2350,4 +2351,11 @@ public Task ExecuteAsync(HttpContext httpContext)
return Task.CompletedTask;
}
}

private sealed class EmptyServiceProvider : IServiceProvider
{
public static IServiceProvider Instance { get; } = new EmptyServiceProvider();

public object? GetService(Type serviceType) => null;
}
}
26 changes: 22 additions & 4 deletions src/Http/Http.Extensions/test/EndpointMetadataContextTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class EndpointMetadataContextTests
[Fact]
public void EndpointMetadataContext_Ctor_ThrowsArgumentNullException_WhenMethodInfoIsNull()
{
Assert.Throws<ArgumentNullException>(() => new EndpointMetadataContext(null, new List<object>(), null));
Assert.Throws<ArgumentNullException>("method", () => new EndpointMetadataContext(null, new List<object>(), null));
}

[Fact]
Expand All @@ -25,13 +25,22 @@ public void EndpointMetadataContext_Ctor_ThrowsArgumentNullException_WhenMetadat
Delegate handler = (int id) => { };
var method = handler.GetMethodInfo();

Assert.Throws<ArgumentNullException>(() => new EndpointMetadataContext(method, null, null));
Assert.Throws<ArgumentNullException>("endpointMetadata", () => new EndpointMetadataContext(method, null, null));
}

[Fact]
public void EndpointMetadataContext_Ctor_ThrowsArgumentNullException_WhenApplicationServicesAreNull()
{
Delegate handler = (int id) => { };
var method = handler.GetMethodInfo();

Assert.Throws<ArgumentNullException>("applicationServices", () => new EndpointMetadataContext(method, new List<object>(), null));
}

[Fact]
public void EndpointParameterMetadataContext_Ctor_ThrowsArgumentNullException_WhenParameterInfoIsNull()
{
Assert.Throws<ArgumentNullException>(() => new EndpointParameterMetadataContext(null, new List<object>(), null));
Assert.Throws<ArgumentNullException>("parameter", () => new EndpointParameterMetadataContext(null, new List<object>(), null));
}

[Fact]
Expand All @@ -40,6 +49,15 @@ public void EndpointParameterMetadataContext_Ctor_ThrowsArgumentNullException_Wh
Delegate handler = (int id) => { };
var parameter = handler.GetMethodInfo().GetParameters()[0];

Assert.Throws<ArgumentNullException>(() => new EndpointParameterMetadataContext(parameter, null, null));
Assert.Throws<ArgumentNullException>("endpointMetadata", () => new EndpointParameterMetadataContext(parameter, null, null));
}

[Fact]
public void EndpointParameterMetadataContext_Ctor_ThrowsArgumentNullException_WhenApplicationServicesAreNull()
{
Delegate handler = (int id) => { };
var parameter = handler.GetMethodInfo().GetParameters()[0];

Assert.Throws<ArgumentNullException>("applicationServices", () => new EndpointParameterMetadataContext(parameter, new List<object>(), null));
}
}
59 changes: 59 additions & 0 deletions src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6025,6 +6025,36 @@ public void Create_AllowsRemovalOfDefaultMetadata_ByParameterTypesImplementingIE
Assert.DoesNotContain(result.EndpointMetadata, m => m is IAcceptsMetadata);
}

[Fact]
public void Create_SetsApplicationServices_OnEndpointMetadataContext()
{
// Arrange
var @delegate = (Todo todo) => new AccessesServicesMetadataResult();
var metadataService = new MetadataService();
var serviceProvider = new ServiceCollection().AddSingleton(metadataService).BuildServiceProvider();

// Act
var result = RequestDelegateFactory.Create(@delegate, new() { ServiceProvider = serviceProvider });

// Assert
Assert.Contains(result.EndpointMetadata, m => m is MetadataService);
}

[Fact]
public void Create_SetsApplicationServices_OnEndpointParameterMetadataContext()
{
// Arrange
var @delegate = (AccessesServicesMetadataBinder parameter1) => "Test";
var metadataService = new MetadataService();
var serviceProvider = new ServiceCollection().AddSingleton(metadataService).BuildServiceProvider();

// Act
var result = RequestDelegateFactory.Create(@delegate, new() { ServiceProvider = serviceProvider });

// Assert
Assert.Contains(result.EndpointMetadata, m => m is MetadataService);
}

private DefaultHttpContext CreateHttpContext()
{
var responseFeature = new TestHttpResponseFeature();
Expand All @@ -6041,6 +6071,35 @@ private DefaultHttpContext CreateHttpContext()
};
}

private record MetadataService;

private class AccessesServicesMetadataResult : IResult, IEndpointMetadataProvider
{
public static void PopulateMetadata(EndpointMetadataContext context)
{
if (context.ApplicationServices?.GetRequiredService<MetadataService>() is { } metadataService)
{
context.EndpointMetadata.Add(metadataService);
}
}

public Task ExecuteAsync(HttpContext httpContext) => Task.CompletedTask;
}

private class AccessesServicesMetadataBinder : IEndpointMetadataProvider
{
public static ValueTask<AccessesServicesMetadataBinder> BindAsync(HttpContext context, ParameterInfo parameter) =>
new(new AccessesServicesMetadataBinder());

public static void PopulateMetadata(EndpointMetadataContext context)
{
if (context.ApplicationServices?.GetRequiredService<MetadataService>() is { } metadataService)
{
context.EndpointMetadata.Add(metadataService);
}
}
}

private class Attribute1 : Attribute
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata()
// Arrange
AcceptedAtRoute<Todo> MyApi() { throw new NotImplementedException(); }
var metadata = new List<object>();
var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, null);
var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, EmptyServiceProvider.Instance);

// Act
PopulateMetadata<AcceptedAtRoute<Todo>>(context);
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata()
// Arrange
AcceptedAtRoute MyApi() { throw new NotImplementedException(); }
var metadata = new List<object>();
var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, null);
var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, EmptyServiceProvider.Instance);

// Act
PopulateMetadata<AcceptedAtRoute>(context);
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Http.Results/test/AcceptedOfTResultTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata()
// Arrange
Accepted<Todo> MyApi() { throw new NotImplementedException(); }
var metadata = new List<object>();
var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, null);
var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, EmptyServiceProvider.Instance);

// Act
PopulateMetadata<Accepted<Todo>>(context);
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Http.Results/test/AcceptedResultTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata()
// Arrange
Accepted MyApi() { throw new NotImplementedException(); }
var metadata = new List<object>();
var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, null);
var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, EmptyServiceProvider.Instance);

// Act
PopulateMetadata<Accepted>(context);
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Http.Results/test/BadRequestOfTResultTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata()
// Arrange
BadRequest<Todo> MyApi() { throw new NotImplementedException(); }
var metadata = new List<object>();
var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, null);
var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, EmptyServiceProvider.Instance);

// Act
PopulateMetadata<BadRequest<Todo>>(context);
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Http.Results/test/BadRequestResultTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata()
// Arrange
BadRequest MyApi() { throw new NotImplementedException(); }
var metadata = new List<object>();
var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, null);
var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, EmptyServiceProvider.Instance);

// Act
PopulateMetadata<BadRequest>(context);
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Http.Results/test/ConflictOfTResultTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata()
// Arrange
Conflict<Todo> MyApi() { throw new NotImplementedException(); }
var metadata = new List<object>();
var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, null);
var context = new EndpointMetadataContext(((Delegate)MyApi).GetMethodInfo(), metadata, EmptyServiceProvider.Instance);

// Act
PopulateMetadata<Conflict<Todo>>(context);
Expand Down
Loading