Expand Up @@ -17,9 +17,9 @@ namespace Microsoft.AspNetCore.Http;

<# foreach (var arity in arities) { #>
[GeneratedCode("TextTemplatingFileGenerator", "")]
internal sealed class RouteHandlerInvocationContext<<# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>T<#=argumentCount#><# if (argumentCount < arity - 1) { #>, <# } #><# } #>> : RouteHandlerInvocationContext, IList<object?>
internal sealed class EndpointFilterInvocationContext<<# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>T<#=argumentCount#><# if (argumentCount < arity - 1) { #>, <# } #><# } #>> : EndpointFilterInvocationContext, IList<object?>
{
internal RouteHandlerInvocationContext(HttpContext httpContext, <# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>T<#=argumentCount#> arg<#=argumentCount#><# if (argumentCount < arity - 1) { #>, <# } #><# } #>)
internal EndpointFilterInvocationContext(HttpContext httpContext, <# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>T<#=argumentCount#> arg<#=argumentCount#><# if (argumentCount < arity - 1) { #>, <# } #><# } #>)
{
HttpContext = httpContext;
<# foreach (var argumentCount in Enumerable.Range(0, arity)) { #> Arg<#=argumentCount#> = arg<#=argumentCount#>;
Expand Down
Expand Up @@ -6,15 +6,15 @@ namespace Microsoft.AspNetCore.Http;
/// <summary>
/// Provides an interface for implementing a filter targetting a route handler.
/// </summary>
public interface IRouteHandlerFilter
public interface IEndpointFilter
{
/// <summary>
/// Implements the core logic associated with the filter given a <see cref="RouteHandlerInvocationContext"/>
/// Implements the core logic associated with the filter given a <see cref="EndpointFilterInvocationContext"/>
/// and the next filter to call in the pipeline.
/// </summary>
/// <param name="context">The <see cref="RouteHandlerInvocationContext"/> associated with the current request/response.</param>
/// <param name="context">The <see cref="EndpointFilterInvocationContext"/> associated with the current request/response.</param>
/// <param name="next">The next filter in the pipeline.</param>
/// <returns>An awaitable result of calling the handler and apply
/// any modifications made by filters in the pipeline.</returns>
ValueTask<object?> InvokeAsync(RouteHandlerInvocationContext context, RouteHandlerFilterDelegate next);
ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next);
}
Expand Up @@ -34,9 +34,9 @@ Microsoft.AspNetCore.Http.HttpResponse</Description>
</ItemGroup>

<ItemGroup>
<None Update="RouteHandlerInvocationContextOfT.Generated.tt">
<None Update="EndpointFilterInvocationContextOfT.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>RouteHandlerInvocationContextOfT.Generated.cs</LastGenOutput>
<LastGenOutput>EndpointFilterInvocationContextOfT.Generated.cs</LastGenOutput>
</None>
</ItemGroup>

Expand All @@ -45,10 +45,10 @@ Microsoft.AspNetCore.Http.HttpResponse</Description>
</ItemGroup>

<ItemGroup>
<Compile Update="RouteHandlerInvocationContextOfT.Generated.cs">
<Compile Update="EndpointFilterInvocationContextOfT.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>RouteHandlerInvocationContextOfT.Generated.tt</DependentUpon>
<DependentUpon>EndpointFilterInvocationContextOfT.Generated.tt</DependentUpon>
</Compile>
</ItemGroup>

Expand Down
36 changes: 18 additions & 18 deletions src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt
@@ -1,25 +1,37 @@
#nullable enable
*REMOVED*abstract Microsoft.AspNetCore.Http.HttpResponse.ContentType.get -> string!
*REMOVED*Microsoft.AspNetCore.Http.EndpointMetadataCollection.Enumerator.Current.get -> object?
abstract Microsoft.AspNetCore.Http.EndpointFilterInvocationContext.Arguments.get -> System.Collections.Generic.IList<object?>!
abstract Microsoft.AspNetCore.Http.EndpointFilterInvocationContext.GetArgument<T>(int index) -> T
abstract Microsoft.AspNetCore.Http.EndpointFilterInvocationContext.HttpContext.get -> Microsoft.AspNetCore.Http.HttpContext!
Microsoft.AspNetCore.Builder.EndpointBuilder.ApplicationServices.get -> System.IServiceProvider!
Microsoft.AspNetCore.Builder.EndpointBuilder.ApplicationServices.set -> void
Microsoft.AspNetCore.Http.AsParametersAttribute
Microsoft.AspNetCore.Http.AsParametersAttribute.AsParametersAttribute() -> void
Microsoft.AspNetCore.Http.CookieBuilder.Extensions.get -> System.Collections.Generic.IList<string!>!
Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext
Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext.DefaultRouteHandlerInvocationContext(Microsoft.AspNetCore.Http.HttpContext! httpContext, params object![]! arguments) -> void
Microsoft.AspNetCore.Http.DefaultEndpointFilterInvocationContext
Microsoft.AspNetCore.Http.DefaultEndpointFilterInvocationContext.DefaultEndpointFilterInvocationContext(Microsoft.AspNetCore.Http.HttpContext! httpContext, params object![]! arguments) -> void
Microsoft.AspNetCore.Http.EndpointFilterDelegate
Microsoft.AspNetCore.Http.EndpointFilterFactoryContext
Microsoft.AspNetCore.Http.EndpointFilterFactoryContext.ApplicationServices.get -> System.IServiceProvider!
Microsoft.AspNetCore.Http.EndpointFilterFactoryContext.EndpointFilterFactoryContext(System.Reflection.MethodInfo! methodInfo, System.Collections.Generic.IList<object!>! endpointMetadata, System.IServiceProvider! applicationServices) -> void
Microsoft.AspNetCore.Http.EndpointFilterFactoryContext.EndpointMetadata.get -> System.Collections.Generic.IList<object!>!
Microsoft.AspNetCore.Http.EndpointFilterFactoryContext.MethodInfo.get -> System.Reflection.MethodInfo!
Microsoft.AspNetCore.Http.EndpointFilterInvocationContext
Microsoft.AspNetCore.Http.EndpointFilterInvocationContext.EndpointFilterInvocationContext() -> void
Microsoft.AspNetCore.Http.EndpointMetadataCollection.Enumerator.Current.get -> object!
Microsoft.AspNetCore.Http.EndpointMetadataCollection.GetRequiredMetadata<T>() -> T!
Microsoft.AspNetCore.Http.IBindableFromHttpContext<TSelf>
Microsoft.AspNetCore.Http.IBindableFromHttpContext<TSelf>.BindAsync(Microsoft.AspNetCore.Http.HttpContext! context, System.Reflection.ParameterInfo! parameter) -> System.Threading.Tasks.ValueTask<TSelf?>
Microsoft.AspNetCore.Http.IContentTypeHttpResult
Microsoft.AspNetCore.Http.IContentTypeHttpResult.ContentType.get -> string?
Microsoft.AspNetCore.Http.IEndpointFilter
Microsoft.AspNetCore.Http.IEndpointFilter.InvokeAsync(Microsoft.AspNetCore.Http.EndpointFilterInvocationContext! context, Microsoft.AspNetCore.Http.EndpointFilterDelegate! next) -> System.Threading.Tasks.ValueTask<object?>
Microsoft.AspNetCore.Http.IFileHttpResult
Microsoft.AspNetCore.Http.IFileHttpResult.ContentType.get -> string?
Microsoft.AspNetCore.Http.IFileHttpResult.FileDownloadName.get -> string?
Microsoft.AspNetCore.Http.INestedHttpResult
Microsoft.AspNetCore.Http.INestedHttpResult.Result.get -> Microsoft.AspNetCore.Http.IResult!
Microsoft.AspNetCore.Http.IRouteHandlerFilter.InvokeAsync(Microsoft.AspNetCore.Http.RouteHandlerInvocationContext! context, Microsoft.AspNetCore.Http.RouteHandlerFilterDelegate! next) -> System.Threading.Tasks.ValueTask<object?>
Microsoft.AspNetCore.Http.IStatusCodeHttpResult
Microsoft.AspNetCore.Http.IStatusCodeHttpResult.StatusCode.get -> int?
Microsoft.AspNetCore.Http.IValueHttpResult
Expand All @@ -30,27 +42,15 @@ Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata
Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata.Name.get -> string?
Microsoft.AspNetCore.Http.Metadata.IRequestSizeLimitMetadata
Microsoft.AspNetCore.Http.Metadata.IRequestSizeLimitMetadata.MaxRequestBodySize.get -> long?
Microsoft.AspNetCore.Http.RouteHandlerContext
Microsoft.AspNetCore.Http.RouteHandlerContext.ApplicationServices.get -> System.IServiceProvider!
Microsoft.AspNetCore.Http.RouteHandlerContext.EndpointMetadata.get -> System.Collections.Generic.IList<object!>!
Microsoft.AspNetCore.Http.RouteHandlerContext.MethodInfo.get -> System.Reflection.MethodInfo!
Microsoft.AspNetCore.Http.RouteHandlerContext.RouteHandlerContext(System.Reflection.MethodInfo! methodInfo, System.Collections.Generic.IList<object!>! endpointMetadata, System.IServiceProvider! applicationServices) -> void
Microsoft.AspNetCore.Http.RouteHandlerFilterDelegate
Microsoft.AspNetCore.Http.RouteHandlerInvocationContext
Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.RouteHandlerInvocationContext() -> void
Microsoft.AspNetCore.Routing.RouteValueDictionary.RouteValueDictionary(Microsoft.AspNetCore.Routing.RouteValueDictionary? dictionary) -> void
Microsoft.AspNetCore.Routing.RouteValueDictionary.RouteValueDictionary(System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string!, object?>>? values) -> void
Microsoft.AspNetCore.Routing.RouteValueDictionary.RouteValueDictionary(System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string!, string?>>? values) -> void
abstract Microsoft.AspNetCore.Http.HttpResponse.ContentType.get -> string?
Microsoft.AspNetCore.Http.Metadata.ISkipStatusCodePagesMetadata
Microsoft.AspNetCore.Http.IRouteHandlerFilter
Microsoft.AspNetCore.Http.Metadata.IEndpointDescriptionMetadata
Microsoft.AspNetCore.Http.Metadata.IEndpointDescriptionMetadata.Description.get -> string!
Microsoft.AspNetCore.Http.Metadata.IEndpointSummaryMetadata
Microsoft.AspNetCore.Http.Metadata.IEndpointSummaryMetadata.Summary.get -> string!
abstract Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.Arguments.get -> System.Collections.Generic.IList<object?>!
abstract Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.GetArgument<T>(int index) -> T
abstract Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.HttpContext.get -> Microsoft.AspNetCore.Http.HttpContext!
override Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext.Arguments.get -> System.Collections.Generic.IList<object?>!
override Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext.GetArgument<T>(int index) -> T
override Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext.HttpContext.get -> Microsoft.AspNetCore.Http.HttpContext!
override Microsoft.AspNetCore.Http.DefaultEndpointFilterInvocationContext.Arguments.get -> System.Collections.Generic.IList<object?>!
override Microsoft.AspNetCore.Http.DefaultEndpointFilterInvocationContext.GetArgument<T>(int index) -> T
override Microsoft.AspNetCore.Http.DefaultEndpointFilterInvocationContext.HttpContext.get -> Microsoft.AspNetCore.Http.HttpContext!
Expand Up @@ -4,12 +4,12 @@

namespace Microsoft.AspNetCore.Http.Abstractions.Tests;

public class RouteHandlerInvocationContextOfTTests
public class EndpointFilterInvocationContextOfTTests
{
[Fact]
public void ProhibitsActionsThatModifyListSize()
{
var context = new RouteHandlerInvocationContext<string, int, bool>(new DefaultHttpContext(), "This is a test", 42, false);
var context = new EndpointFilterInvocationContext<string, int, bool>(new DefaultHttpContext(), "This is a test", 42, false);
Assert.Throws<NotSupportedException>(() => context.Add("string"));
Assert.Throws<NotSupportedException>(() => context.Insert(0, "string"));
Assert.Throws<NotSupportedException>(() => context.RemoveAt(0));
Expand All @@ -20,7 +20,7 @@ public void ProhibitsActionsThatModifyListSize()
[Fact]
public void ThrowsExceptionForInvalidCastOnGetArgument()
{
var context = new RouteHandlerInvocationContext<string, int, bool, Todo>(new DefaultHttpContext(), "This is a test", 42, false, new Todo());
var context = new EndpointFilterInvocationContext<string, int, bool, Todo>(new DefaultHttpContext(), "This is a test", 42, false, new Todo());
Assert.Throws<InvalidCastException>(() => context.GetArgument<string>(1));
Assert.Throws<InvalidCastException>(() => context.GetArgument<int>(0));
Assert.Throws<InvalidCastException>(() => context.GetArgument<string>(3));
Expand All @@ -31,22 +31,22 @@ public void ThrowsExceptionForInvalidCastOnGetArgument()
[Fact]
public void SetterAllowsInPlaceModificationOfParameters()
{
var context = new RouteHandlerInvocationContext<string, int, bool, Todo>(new DefaultHttpContext(), "This is a test", 42, false, new Todo());
var context = new EndpointFilterInvocationContext<string, int, bool, Todo>(new DefaultHttpContext(), "This is a test", 42, false, new Todo());
context[0] = "Foo";
Assert.Equal("Foo", context.GetArgument<string>(0));
}

[Fact]
public void SetterDoesNotAllowModificationOfParameterType()
{
var context = new RouteHandlerInvocationContext<string, int, bool, Todo>(new DefaultHttpContext(), "This is a test", 42, false, new Todo());
var context = new EndpointFilterInvocationContext<string, int, bool, Todo>(new DefaultHttpContext(), "This is a test", 42, false, new Todo());
Assert.Throws<InvalidCastException>(() => context[0] = 4);
}

[Fact]
public void AllowsEnumerationOfParameters()
{
var context = new RouteHandlerInvocationContext<string, int, bool, Todo>(new DefaultHttpContext(), "This is a test", 42, false, new Todo());
var context = new EndpointFilterInvocationContext<string, int, bool, Todo>(new DefaultHttpContext(), "This is a test", 42, false, new Todo());
var enumeratedCount = 0;
foreach (var parameter in context)
{
Expand All @@ -60,7 +60,7 @@ public void AllowsEnumerationOfParameters()
[Fact]
public void HandlesMismatchedNullabilityOnTypeParams()
{
var context = new RouteHandlerInvocationContext<string?, int?, bool?, Todo?>(new DefaultHttpContext(), null, null, null, null);
var context = new EndpointFilterInvocationContext<string?, int?, bool?, Todo?>(new DefaultHttpContext(), null, null, null, null);
// Mismatched reference types will resolve as null
Assert.Null(context.GetArgument<string>(0));
Assert.Null(context.GetArgument<Todo>(3));
Expand All @@ -72,8 +72,8 @@ public void HandlesMismatchedNullabilityOnTypeParams()
[Fact]
public void GeneratedCodeIsUpToDate()
{
var currentContentPath = Path.Combine(AppContext.BaseDirectory, "Shared", "GeneratedContent", "RouteHandlerInvocationContextOfT.Generated.cs");
var templatePath = Path.Combine(AppContext.BaseDirectory, "Shared", "GeneratedContent", "RouteHandlerInvocationContextOfT.Generated.tt");
var currentContentPath = Path.Combine(AppContext.BaseDirectory, "Shared", "GeneratedContent", "EndpointFilterInvocationContextOfT.Generated.cs");
var templatePath = Path.Combine(AppContext.BaseDirectory, "Shared", "GeneratedContent", "EndpointFilterInvocationContextOfT.Generated.tt");

var generator = new TemplateGenerator();
var compiledTemplate = generator.CompileTemplate(File.ReadAllText(templatePath));
Expand Down
Expand Up @@ -16,9 +16,9 @@
</ItemGroup>

<ItemGroup>
<Content Include="$(MSBuildThisFileDirectory)\..\src\RouteHandlerInvocationContextOfT.Generated.tt" LinkBase="Shared\GeneratedContent" CopyToOutputDirectory="PreserveNewest" />
<Content Include="$(MSBuildThisFileDirectory)\..\src\RouteHandlerInvocationContextOfT.Generated.cs" LinkBase="Shared\GeneratedContent" CopyToOutputDirectory="PreserveNewest" />
<Compile Update="RouteHandlerInvocationContextOfT.Generated.cs" Link="Shared\GeneratedContent\%(RecursiveDir)%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" />
<Content Include="$(MSBuildThisFileDirectory)\..\src\EndpointFilterInvocationContextOfT.Generated.tt" LinkBase="Shared\GeneratedContent" CopyToOutputDirectory="PreserveNewest" />
<Content Include="$(MSBuildThisFileDirectory)\..\src\EndpointFilterInvocationContextOfT.Generated.cs" LinkBase="Shared\GeneratedContent" CopyToOutputDirectory="PreserveNewest" />
<Compile Update="EndpointFilterInvocationContextOfT.Generated.cs" Link="Shared\GeneratedContent\%(RecursiveDir)%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

</Project>
4 changes: 2 additions & 2 deletions src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt
Expand Up @@ -13,10 +13,10 @@ Microsoft.AspNetCore.Http.Metadata.IEndpointMetadataProvider
Microsoft.AspNetCore.Http.Metadata.IEndpointMetadataProvider.PopulateMetadata(Microsoft.AspNetCore.Http.Metadata.EndpointMetadataContext! context) -> void
Microsoft.AspNetCore.Http.Metadata.IEndpointParameterMetadataProvider
Microsoft.AspNetCore.Http.Metadata.IEndpointParameterMetadataProvider.PopulateMetadata(Microsoft.AspNetCore.Http.Metadata.EndpointParameterMetadataContext! parameterContext) -> void
Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions.EndpointFilterFactories.get -> System.Collections.Generic.IReadOnlyList<System.Func<Microsoft.AspNetCore.Http.EndpointFilterFactoryContext!, Microsoft.AspNetCore.Http.EndpointFilterDelegate!, Microsoft.AspNetCore.Http.EndpointFilterDelegate!>!>?
Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions.EndpointFilterFactories.init -> void
Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions.EndpointMetadata.get -> System.Collections.Generic.IList<object!>?
Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions.EndpointMetadata.init -> void
Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions.RouteHandlerFilterFactories.get -> System.Collections.Generic.IReadOnlyList<System.Func<Microsoft.AspNetCore.Http.RouteHandlerContext!, Microsoft.AspNetCore.Http.RouteHandlerFilterDelegate!, Microsoft.AspNetCore.Http.RouteHandlerFilterDelegate!>!>?
Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions.RouteHandlerFilterFactories.init -> void
Microsoft.Extensions.DependencyInjection.RouteHandlerJsonServiceExtensions
static Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(this Microsoft.AspNetCore.Http.HttpRequest! request, System.Type! type, System.Text.Json.Serialization.JsonSerializerContext! context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask<object?>
static Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync<TValue>(this Microsoft.AspNetCore.Http.HttpRequest! request, System.Text.Json.Serialization.Metadata.JsonTypeInfo<TValue>! jsonTypeInfo, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask<TValue?>
Expand Down
72 changes: 36 additions & 36 deletions src/Http/Http.Extensions/src/RequestDelegateFactory.cs
Expand Up @@ -102,15 +102,15 @@ public static partial class RequestDelegateFactory
private static readonly BinaryExpression TempSourceStringNullExpr = Expression.Equal(TempSourceStringExpr, Expression.Constant(null));
private static readonly UnaryExpression TempSourceStringIsNotNullOrEmptyExpr = Expression.Not(Expression.Call(StringIsNullOrEmptyMethod, TempSourceStringExpr));

private static readonly ConstructorInfo DefaultRouteHandlerInvocationContextConstructor = typeof(DefaultRouteHandlerInvocationContext).GetConstructor(new[] { typeof(HttpContext), typeof(object[]) })!;
private static readonly MethodInfo RouteHandlerInvocationContextGetArgument = typeof(RouteHandlerInvocationContext).GetMethod(nameof(RouteHandlerInvocationContext.GetArgument))!;
private static readonly ConstructorInfo DefaultEndpointFilterInvocationContextConstructor = typeof(DefaultEndpointFilterInvocationContext).GetConstructor(new[] { typeof(HttpContext), typeof(object[]) })!;
private static readonly MethodInfo EndpointFilterInvocationContextGetArgument = typeof(EndpointFilterInvocationContext).GetMethod(nameof(EndpointFilterInvocationContext.GetArgument))!;
private static readonly PropertyInfo ListIndexer = typeof(IList<object>).GetProperty("Item")!;
private static readonly ParameterExpression FilterContextExpr = Expression.Parameter(typeof(RouteHandlerInvocationContext), "context");
private static readonly MemberExpression FilterContextHttpContextExpr = Expression.Property(FilterContextExpr, typeof(RouteHandlerInvocationContext).GetProperty(nameof(RouteHandlerInvocationContext.HttpContext))!);
private static readonly MemberExpression FilterContextArgumentsExpr = Expression.Property(FilterContextExpr, typeof(RouteHandlerInvocationContext).GetProperty(nameof(RouteHandlerInvocationContext.Arguments))!);
private static readonly ParameterExpression FilterContextExpr = Expression.Parameter(typeof(EndpointFilterInvocationContext), "context");
private static readonly MemberExpression FilterContextHttpContextExpr = Expression.Property(FilterContextExpr, typeof(EndpointFilterInvocationContext).GetProperty(nameof(EndpointFilterInvocationContext.HttpContext))!);
private static readonly MemberExpression FilterContextArgumentsExpr = Expression.Property(FilterContextExpr, typeof(EndpointFilterInvocationContext).GetProperty(nameof(EndpointFilterInvocationContext.Arguments))!);
private static readonly MemberExpression FilterContextHttpContextResponseExpr = Expression.Property(FilterContextHttpContextExpr, typeof(HttpContext).GetProperty(nameof(HttpContext.Response))!);
private static readonly MemberExpression FilterContextHttpContextStatusCodeExpr = Expression.Property(FilterContextHttpContextResponseExpr, typeof(HttpResponse).GetProperty(nameof(HttpResponse.StatusCode))!);
private static readonly ParameterExpression InvokedFilterContextExpr = Expression.Parameter(typeof(RouteHandlerInvocationContext), "filterContext");
private static readonly ParameterExpression InvokedFilterContextExpr = Expression.Parameter(typeof(EndpointFilterInvocationContext), "filterContext");

private static readonly string[] DefaultAcceptsContentType = new[] { "application/json" };
private static readonly string[] FormFileContentType = new[] { "multipart/form-data" };
Expand Down Expand Up @@ -208,7 +208,7 @@ private static FactoryContext CreateFactoryContext(RequestDelegateFactoryOptions
RouteParameters = options?.RouteParameterNames?.ToList(),
ThrowOnBadRequest = options?.ThrowOnBadRequest ?? false,
DisableInferredFromBody = options?.DisableInferBodyFromParameters ?? false,
FilterFactories = options?.RouteHandlerFilterFactories?.ToList(),
FilterFactories = options?.EndpointFilterFactories?.ToList(),
Metadata = options?.EndpointMetadata ?? new List<object>(),
};
}
Expand Down Expand Up @@ -252,7 +252,7 @@ private static IReadOnlyList<object> AsReadOnlyList(IList<object> metadata)
factoryContext.ServiceProvider,
CollectionsMarshal.AsSpan(factoryContext.Parameters));

RouteHandlerFilterDelegate? filterPipeline = null;
EndpointFilterDelegate? filterPipeline = null;

// If there are filters registered on the route handler, then we update the method call and
// return type associated with the request to allow for the filter invocation pipeline.
Expand All @@ -262,15 +262,15 @@ private static IReadOnlyList<object> AsReadOnlyList(IList<object> metadata)

if (filterPipeline is not null)
{
Expression<Func<RouteHandlerInvocationContext, ValueTask<object?>>> invokePipeline = (context) => filterPipeline(context);
Expression<Func<EndpointFilterInvocationContext, ValueTask<object?>>> invokePipeline = (context) => filterPipeline(context);
returnType = typeof(ValueTask<object?>);
// var filterContext = new RouteHandlerInvocationContext<string, int>(httpContext, name_local, int_local);
// var filterContext = new EndpointFilterInvocationContext<string, int>(httpContext, name_local, int_local);
// invokePipeline.Invoke(filterContext);
factoryContext.MethodCall = Expression.Block(
new[] { InvokedFilterContextExpr },
Expression.Assign(
InvokedFilterContextExpr,
CreateRouteHandlerInvocationContextBase(factoryContext)),
CreateEndpointFilterInvocationContextBase(factoryContext)),
Expression.Invoke(invokePipeline, InvokedFilterContextExpr)
);
}
Expand Down Expand Up @@ -298,7 +298,7 @@ private static IReadOnlyList<object> AsReadOnlyList(IList<object> metadata)
return HandleRequestBodyAndCompileRequestDelegate(responseWritingMethodCall, factoryContext);
}

private static RouteHandlerFilterDelegate? CreateFilterPipeline(MethodInfo methodInfo, Expression? targetExpression, FactoryContext factoryContext, Expression<Func<HttpContext, object?>>? targetFactory)
private static EndpointFilterDelegate? CreateFilterPipeline(MethodInfo methodInfo, Expression? targetExpression, FactoryContext factoryContext, Expression<Func<HttpContext, object?>>? targetFactory)
{
Debug.Assert(factoryContext.FilterFactories is not null);
// httpContext.Response.StatusCode >= 400
Expand All @@ -314,10 +314,10 @@ private static IReadOnlyList<object> AsReadOnlyList(IList<object> metadata)
// When `handler` returns an object, we generate the following wrapper
// to convert it to `ValueTask<object?>` as expected in the filter
// pipeline.
// ValueTask<object?>.FromResult(handler(RouteHandlerInvocationContext.GetArgument<string>(0), RouteHandlerInvocationContext.GetArgument<int>(1)));
// ValueTask<object?>.FromResult(handler(EndpointFilterInvocationContext.GetArgument<string>(0), EndpointFilterInvocationContext.GetArgument<int>(1)));
// When the `handler` is a generic Task or ValueTask we await the task and
// create a `ValueTask<object?> from the resulting value.
// new ValueTask<object?>(await handler(RouteHandlerInvocationContext.GetArgument<string>(0), RouteHandlerInvocationContext.GetArgument<int>(1)));
// new ValueTask<object?>(await handler(EndpointFilterInvocationContext.GetArgument<string>(0), EndpointFilterInvocationContext.GetArgument<int>(1)));
// When the `handler` returns a void or a void-returning Task, then we return an EmptyHttpResult
// to as a ValueTask<object?>
// }
Expand All @@ -333,13 +333,13 @@ private static IReadOnlyList<object> AsReadOnlyList(IList<object> metadata)
: Expression.Assign(TargetExpr, Expression.Invoke(targetFactory, FilterContextHttpContextExpr)),
handlerReturnMapping
);
var filteredInvocation = Expression.Lambda<RouteHandlerFilterDelegate>(
var filteredInvocation = Expression.Lambda<EndpointFilterDelegate>(
Expression.Condition(
Expression.GreaterThanOrEqual(FilterContextHttpContextStatusCodeExpr, Expression.Constant(400)),
CompletedValueTaskExpr,
handlerInvocation),
FilterContextExpr).Compile();
var routeHandlerContext = new RouteHandlerContext(
var routeHandlerContext = new EndpointFilterFactoryContext(
methodInfo,
factoryContext.Metadata,
factoryContext.ServiceProvider ?? EmptyServiceProvider.Instance);
Expand Down Expand Up @@ -428,16 +428,16 @@ private static Expression MapHandlerReturnTypeToValueTask(Expression methodCall,
return ExecuteAwaited(task);
}

private static Expression CreateRouteHandlerInvocationContextBase(FactoryContext factoryContext)
private static Expression CreateEndpointFilterInvocationContextBase(FactoryContext factoryContext)
{
// In the event that a constructor matching the arity of the
// provided parameters is not found, we fall back to using the
// non-generic implementation of RouteHandlerInvocationContext.
// non-generic implementation of EndpointFilterInvocationContext.
Expression paramArray = factoryContext.BoxedArgs.Length > 0
? Expression.NewArrayInit(typeof(object), factoryContext.BoxedArgs)
: Expression.Call(ArrayEmptyOfObjectMethod);
var fallbackConstruction = Expression.New(
DefaultRouteHandlerInvocationContextConstructor,
DefaultEndpointFilterInvocationContextConstructor,
new Expression[] { HttpContextExpr, paramArray });

if (!RuntimeFeature.IsDynamicCodeCompiled)
Expand All @@ -453,33 +453,33 @@ private static Expression CreateRouteHandlerInvocationContextBase(FactoryContext

var constructorType = factoryContext.ArgumentTypes?.Length switch
{
1 => typeof(RouteHandlerInvocationContext<>),
2 => typeof(RouteHandlerInvocationContext<,>),
3 => typeof(RouteHandlerInvocationContext<,,>),
4 => typeof(RouteHandlerInvocationContext<,,,>),
5 => typeof(RouteHandlerInvocationContext<,,,,>),
6 => typeof(RouteHandlerInvocationContext<,,,,,>),
7 => typeof(RouteHandlerInvocationContext<,,,,,,>),
8 => typeof(RouteHandlerInvocationContext<,,,,,,,>),
9 => typeof(RouteHandlerInvocationContext<,,,,,,,,>),
10 => typeof(RouteHandlerInvocationContext<,,,,,,,,,>),
_ => typeof(DefaultRouteHandlerInvocationContext)
1 => typeof(EndpointFilterInvocationContext<>),
2 => typeof(EndpointFilterInvocationContext<,>),
3 => typeof(EndpointFilterInvocationContext<,,>),
4 => typeof(EndpointFilterInvocationContext<,,,>),
5 => typeof(EndpointFilterInvocationContext<,,,,>),
6 => typeof(EndpointFilterInvocationContext<,,,,,>),
7 => typeof(EndpointFilterInvocationContext<,,,,,,>),
8 => typeof(EndpointFilterInvocationContext<,,,,,,,>),
9 => typeof(EndpointFilterInvocationContext<,,,,,,,,>),
10 => typeof(EndpointFilterInvocationContext<,,,,,,,,,>),
_ => typeof(DefaultEndpointFilterInvocationContext)
};

if (constructorType.IsGenericType)
{
var constructor = constructorType.MakeGenericType(factoryContext.ArgumentTypes!).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).SingleOrDefault();
if (constructor == null)
{
// new RouteHandlerInvocationContext(httpContext, (object)name_local, (object)int_local);
// new EndpointFilterInvocationContext(httpContext, (object)name_local, (object)int_local);
return fallbackConstruction;
}

// new RouteHandlerInvocationContext<string, int>(httpContext, name_local, int_local);
// new EndpointFilterInvocationContext<string, int>(httpContext, name_local, int_local);
return Expression.New(constructor, arguments);
}

// new RouteHandlerInvocationContext(httpContext, (object)name_local, (object)int_local);
// new EndpointFilterInvocationContext(httpContext, (object)name_local, (object)int_local);
return fallbackConstruction;
}

Expand Down Expand Up @@ -564,11 +564,11 @@ private static Expression[] CreateArguments(ParameterInfo[]? parameters, Factory
if (RuntimeFeature.IsDynamicCodeSupported)
{
// Register expressions containing the boxed and unboxed variants
// of the route handler's arguments for use in RouteHandlerInvocationContext
// of the route handler's arguments for use in EndpointFilterInvocationContext
// construction and route handler invocation.
// context.GetArgument<string>(0)
// (string, name_local), (int, int_local)
factoryContext.ContextArgAccess.Add(Expression.Call(FilterContextExpr, RouteHandlerInvocationContextGetArgument.MakeGenericMethod(parameters[i].ParameterType), Expression.Constant(i)));
factoryContext.ContextArgAccess.Add(Expression.Call(FilterContextExpr, EndpointFilterInvocationContextGetArgument.MakeGenericMethod(parameters[i].ParameterType), Expression.Constant(i)));
}
else
{
Expand Down Expand Up @@ -2126,7 +2126,7 @@ private sealed class FactoryContext
public Type[] ArgumentTypes { get; set; } = Array.Empty<Type>();
public Expression[] ArgumentExpressions { get; set; } = Array.Empty<Expression>();
public Expression[] BoxedArgs { get; set; } = Array.Empty<Expression>();
public List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>? FilterFactories { get; init; }
public List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>? FilterFactories { get; init; }
public bool FilterFactoriesHaveRunWithoutModifyingPerRequestBehavior { get; set; }

public List<ParameterInfo> Parameters { get; set; } = new();
Expand Down
Expand Up @@ -36,7 +36,7 @@ public sealed class RequestDelegateFactoryOptions
/// <summary>
/// The list of filters that must run in the pipeline for a given route handler.
/// </summary>
public IReadOnlyList<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>? RouteHandlerFilterFactories { get; init; }
public IReadOnlyList<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>? EndpointFilterFactories { get; init; }

/// <summary>
/// The mutable initial endpoint metadata to add as part of the creation of the <see cref="RequestDelegateResult.RequestDelegate"/>. In most cases,
Expand Down
42 changes: 21 additions & 21 deletions src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs
Expand Up @@ -4889,7 +4889,7 @@ string HelloName(string name)
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) => async (context) =>
{
Expand Down Expand Up @@ -4921,7 +4921,7 @@ public async Task RequestDelegateFactory_InvokesFilters_OnDelegateWithTarget()
// Act
var factoryResult = RequestDelegateFactory.Create((string name) => $"Hello, {name}!", new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) => async (context) =>
{
Expand Down Expand Up @@ -4963,7 +4963,7 @@ public async Task RequestDelegateFactory_InvokesFilters_OnMethodInfoWithNullTarg
// Act
var factoryResult = RequestDelegateFactory.Create(methodInfo!, null, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) => async (context) =>
{
Expand Down Expand Up @@ -5006,7 +5006,7 @@ public async Task RequestDelegateFactory_InvokesFilters_OnMethodInfoWithProvided
};
var factoryResult = RequestDelegateFactory.Create(methodInfo!, targetFactory, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) => async (context) =>
{
Expand Down Expand Up @@ -5043,7 +5043,7 @@ string HelloName(string name)
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>() {
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>() {
(routeHandlerContext, next) => async (context) =>
{
if (context.HttpContext.Response.StatusCode == 400)
Expand Down Expand Up @@ -5089,7 +5089,7 @@ string HelloName(string name, int age)
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) => async (context) =>
{
Expand Down Expand Up @@ -5137,7 +5137,7 @@ string HelloName(string name)
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) =>
{
Expand Down Expand Up @@ -5192,7 +5192,7 @@ string HelloName(IFormFileCollection formFiles)
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) =>
{
Expand Down Expand Up @@ -5243,7 +5243,7 @@ string PrintTodo(Todo todo)
// Act
var factoryResult = RequestDelegateFactory.Create(PrintTodo, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) => async (context) =>
{
Expand Down Expand Up @@ -5284,7 +5284,7 @@ string HelloName(string name)
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) => async (context) =>
{
Expand Down Expand Up @@ -5327,7 +5327,7 @@ string HelloName(string name)
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) => async (context) =>
{
Expand Down Expand Up @@ -5396,7 +5396,7 @@ public async Task CanInvokeFilter_OnTaskOfTReturningHandler(Delegate @delegate)
// Act
var factoryResult = RequestDelegateFactory.Create(@delegate, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) => async (context) =>
{
Expand Down Expand Up @@ -5454,7 +5454,7 @@ public async Task CanInvokeFilter_OnValueTaskOfTReturningHandler(Delegate @deleg
// Act
var factoryResult = RequestDelegateFactory.Create(@delegate, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) => async (context) =>
{
Expand Down Expand Up @@ -5519,7 +5519,7 @@ public async Task CanInvokeFilter_OnVoidReturningHandler(Delegate @delegate)
// Act
var factoryResult = RequestDelegateFactory.Create(@delegate, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) => async (context) =>
{
Expand Down Expand Up @@ -5553,7 +5553,7 @@ async Task HandlerWithTaskAwait(HttpContext c)
// Act
var factoryResult = RequestDelegateFactory.Create(HandlerWithTaskAwait, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) => async (context) =>
{
Expand Down Expand Up @@ -5620,7 +5620,7 @@ public async Task CanInvokeFilter_OnHandlerReturningTasksOfStruct(Delegate @dele
// Act
var factoryResult = RequestDelegateFactory.Create(@delegate, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) => async (context) =>
{
Expand Down Expand Up @@ -5656,11 +5656,11 @@ string HelloName(int? one, string? two, int? three, string? four, int? five, boo
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) => async (context) =>
{
Assert.IsType<DefaultRouteHandlerInvocationContext>(context);
Assert.IsType<DefaultEndpointFilterInvocationContext>(context);
Assert.Equal(11, context.Arguments.Count);
return await next(context);
}
Expand All @@ -5687,11 +5687,11 @@ string HelloName()
// Act
var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) => async (context) =>
{
Assert.IsType<DefaultRouteHandlerInvocationContext>(context);
Assert.IsType<DefaultEndpointFilterInvocationContext>(context);
Assert.Equal(0, context.Arguments.Count);
return await next(context);
}
Expand Down Expand Up @@ -6081,7 +6081,7 @@ public void Create_ReturnsSameRequestDelegatePassedIn_IfNotModifiedByFilters()

RequestDelegateFactoryOptions options = new()
{
RouteHandlerFilterFactories = new List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>()
EndpointFilterFactories = new List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>()
{
(routeHandlerContext, next) =>
{
Expand Down
Expand Up @@ -9,51 +9,50 @@
namespace Microsoft.AspNetCore.Http;

/// <summary>
/// Extension methods for adding <see cref="IRouteHandlerFilter"/> to a route handler.
/// Extension methods for adding <see cref="IEndpointFilter"/> to a route handler.
/// </summary>
public static class RouteHandlerFilterExtensions
public static class EndpointFilterExtensions
{
/// <summary>
/// Registers a filter onto the route handler.
/// </summary>
/// <param name="builder">The <see cref="RouteHandlerBuilder"/>.</param>
/// <param name="filter">The <see cref="IRouteHandlerFilter"/> to register.</param>
/// <param name="filter">The <see cref="IEndpointFilter"/> to register.</param>
/// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the route handler.</returns>

[RequiresUnreferencedCode(EndpointRouteBuilderExtensions.MapEndpointTrimmerWarning)]
public static TBuilder AddRouteHandlerFilter<TBuilder>(this TBuilder builder, IRouteHandlerFilter filter) where TBuilder : IEndpointConventionBuilder =>
builder.AddRouteHandlerFilter((routeHandlerContext, next) => (context) => filter.InvokeAsync(context, next));
public static TBuilder AddEndpointFilter<TBuilder>(this TBuilder builder, IEndpointFilter filter) where TBuilder : IEndpointConventionBuilder =>
builder.AddEndpointFilter((routeHandlerContext, next) => (context) => filter.InvokeAsync(context, next));

/// <summary>
/// Registers a filter of type <typeparamref name="TFilterType"/> onto the route handler.
/// </summary>
/// <typeparam name="TBuilder">The type of the <see cref="IEndpointConventionBuilder"/> to configure.</typeparam>
/// <typeparam name="TFilterType">The type of the <see cref="IRouteHandlerFilter"/> to register.</typeparam>
/// <typeparam name="TFilterType">The type of the <see cref="IEndpointFilter"/> to register.</typeparam>
/// <param name="builder">The <see cref="RouteHandlerBuilder"/>.</param>
/// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the route handler.</returns>
[RequiresUnreferencedCode(EndpointRouteBuilderExtensions.MapEndpointTrimmerWarning)]
public static TBuilder AddRouteHandlerFilter<TBuilder, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFilterType>(this TBuilder builder)
public static TBuilder AddEndpointFilter<TBuilder, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFilterType>(this TBuilder builder)
where TBuilder : IEndpointConventionBuilder
where TFilterType : IRouteHandlerFilter
where TFilterType : IEndpointFilter
{
// We call `CreateFactory` twice here since the `CreateFactory` API does not support optional arguments.
// See https://github.com/dotnet/runtime/issues/67309 for more info.
ObjectFactory filterFactory;
try
{
filterFactory = ActivatorUtilities.CreateFactory(typeof(TFilterType), new[] { typeof(RouteHandlerContext) });
filterFactory = ActivatorUtilities.CreateFactory(typeof(TFilterType), new[] { typeof(EndpointFilterFactoryContext) });
}
catch (InvalidOperationException)
{
filterFactory = ActivatorUtilities.CreateFactory(typeof(TFilterType), Type.EmptyTypes);
}

builder.AddRouteHandlerFilter((routeHandlerContext, next) =>
builder.AddEndpointFilter((routeHandlerContext, next) =>
{
var invokeArguments = new[] { routeHandlerContext };
return (context) =>
{
var filter = (IRouteHandlerFilter)filterFactory.Invoke(context.HttpContext.RequestServices, invokeArguments);
var filter = (IEndpointFilter)filterFactory.Invoke(context.HttpContext.RequestServices, invokeArguments);
return filter.InvokeAsync(context, next);
};
});
Expand All @@ -63,29 +62,29 @@ public static class RouteHandlerFilterExtensions
/// <summary>
/// Registers a filter of type <typeparamref name="TFilterType"/> onto the route handler.
/// </summary>
/// <typeparam name="TFilterType">The type of the <see cref="IRouteHandlerFilter"/> to register.</typeparam>
/// <typeparam name="TFilterType">The type of the <see cref="IEndpointFilter"/> to register.</typeparam>
/// <param name="builder">The <see cref="RouteHandlerBuilder"/>.</param>
/// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the route handler.</returns>
[RequiresUnreferencedCode(EndpointRouteBuilderExtensions.MapEndpointTrimmerWarning)]
public static RouteHandlerBuilder AddRouteHandlerFilter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFilterType>(this RouteHandlerBuilder builder)
where TFilterType : IRouteHandlerFilter
public static RouteHandlerBuilder AddEndpointFilter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFilterType>(this RouteHandlerBuilder builder)
where TFilterType : IEndpointFilter
{
// We have a RouteHandlerBuiler and GroupRouteBuilder-specific AddFilter methods for convenience so you don't have to specify both arguments most the time.
return builder.AddRouteHandlerFilter<RouteHandlerBuilder, TFilterType>();
return builder.AddEndpointFilter<RouteHandlerBuilder, TFilterType>();
}

/// <summary>
/// Registers a filter of type <typeparamref name="TFilterType"/> onto the route handler.
/// </summary>
/// <typeparam name="TFilterType">The type of the <see cref="IRouteHandlerFilter"/> to register.</typeparam>
/// <typeparam name="TFilterType">The type of the <see cref="IEndpointFilter"/> to register.</typeparam>
/// <param name="builder">The <see cref="RouteHandlerBuilder"/>.</param>
/// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the route handler.</returns>
[RequiresUnreferencedCode(EndpointRouteBuilderExtensions.MapEndpointTrimmerWarning)]
public static RouteGroupBuilder AddRouteHandlerFilter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFilterType>(this RouteGroupBuilder builder)
where TFilterType : IRouteHandlerFilter
public static RouteGroupBuilder AddEndpointFilter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFilterType>(this RouteGroupBuilder builder)
where TFilterType : IEndpointFilter
{
// We have a RouteHandlerBuiler and GroupRouteBuilder-specific AddFilter methods for convenience so you don't have to specify both arguments most the time.
return builder.AddRouteHandlerFilter<RouteGroupBuilder, TFilterType>();
return builder.AddEndpointFilter<RouteGroupBuilder, TFilterType>();
}

/// <summary>
Expand All @@ -95,10 +94,10 @@ public static class RouteHandlerFilterExtensions
/// <param name="routeHandlerFilter">A method representing the core logic of the filter.</param>
/// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the route handler.</returns>
[RequiresUnreferencedCode(EndpointRouteBuilderExtensions.MapEndpointTrimmerWarning)]
public static TBuilder AddRouteHandlerFilter<TBuilder>(this TBuilder builder, Func<RouteHandlerInvocationContext, RouteHandlerFilterDelegate, ValueTask<object?>> routeHandlerFilter)
public static TBuilder AddEndpointFilter<TBuilder>(this TBuilder builder, Func<EndpointFilterInvocationContext, EndpointFilterDelegate, ValueTask<object?>> routeHandlerFilter)
where TBuilder : IEndpointConventionBuilder
{
return builder.AddRouteHandlerFilter((routeHandlerContext, next) => (context) => routeHandlerFilter(context, next));
return builder.AddEndpointFilter((routeHandlerContext, next) => (context) => routeHandlerFilter(context, next));
}

/// <summary>
Expand All @@ -108,7 +107,7 @@ public static TBuilder AddRouteHandlerFilter<TBuilder>(this TBuilder builder, Fu
/// <param name="filterFactory">A method representing the logic for constructing the filter.</param>
/// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the route handler.</returns>
[RequiresUnreferencedCode(EndpointRouteBuilderExtensions.MapEndpointTrimmerWarning)]
public static TBuilder AddRouteHandlerFilter<TBuilder>(this TBuilder builder, Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate> filterFactory)
public static TBuilder AddEndpointFilter<TBuilder>(this TBuilder builder, Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate> filterFactory)
where TBuilder : IEndpointConventionBuilder
{
builder.Add(endpointBuilder =>
Expand All @@ -118,8 +117,8 @@ public static TBuilder AddRouteHandlerFilter<TBuilder>(this TBuilder builder, Fu
return;
}
routeEndpointBuilder.RouteHandlerFilterFactories ??= new();
routeEndpointBuilder.RouteHandlerFilterFactories.Add(filterFactory);
routeEndpointBuilder.EndpointFilterFactories ??= new();
routeEndpointBuilder.EndpointFilterFactories.Add(filterFactory);
});

return builder;
Expand Down
4 changes: 2 additions & 2 deletions src/Http/Routing/src/CompositeEndpointDataSource.cs
Expand Up @@ -76,7 +76,7 @@ public override IReadOnlyList<Endpoint> Endpoints
}

/// <inheritdoc/>
public override IReadOnlyList<Endpoint> GetEndpointGroup(RouteGroupContext context)
public override IReadOnlyList<Endpoint> GetGroupedEndpoints(RouteGroupContext context)
{
if (_dataSources.Count is 0)
{
Expand All @@ -90,7 +90,7 @@ public override IReadOnlyList<Endpoint> GetEndpointGroup(RouteGroupContext conte

foreach (var dataSource in _dataSources)
{
groupedEndpoints.AddRange(dataSource.GetEndpointGroup(context));
groupedEndpoints.AddRange(dataSource.GetGroupedEndpoints(context));
}

// There's no need to cache these the way we do with _endpoints. This is only ever used to get intermediate results.
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Routing/src/EndpointDataSource.cs
Expand Up @@ -32,7 +32,7 @@ public abstract class EndpointDataSource
/// <returns>
/// Returns a read-only collection of <see cref="Endpoint"/> instances given the specified group <see cref="RouteGroupContext.Prefix"/> and <see cref="RouteGroupContext.Conventions"/>.
/// </returns>
public virtual IReadOnlyList<Endpoint> GetEndpointGroup(RouteGroupContext context)
public virtual IReadOnlyList<Endpoint> GetGroupedEndpoints(RouteGroupContext context)
{
// Only evaluate Endpoints once per call.
var endpoints = Endpoints;
Expand Down
18 changes: 9 additions & 9 deletions src/Http/Routing/src/PublicAPI.Unshipped.txt
@@ -1,5 +1,5 @@
#nullable enable
Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions
Microsoft.AspNetCore.Http.EndpointFilterExtensions
Microsoft.AspNetCore.Routing.CompositeEndpointDataSource.Dispose() -> void
Microsoft.AspNetCore.Routing.RouteGroupBuilder
Microsoft.AspNetCore.Routing.RouteGroupContext
Expand All @@ -9,22 +9,22 @@ Microsoft.AspNetCore.Routing.RouteGroupContext.Prefix.get -> Microsoft.AspNetCor
Microsoft.AspNetCore.Routing.RouteGroupContext.RouteGroupContext(Microsoft.AspNetCore.Routing.Patterns.RoutePattern! prefix, System.Collections.Generic.IReadOnlyList<System.Action<Microsoft.AspNetCore.Builder.EndpointBuilder!>!>! conventions, System.IServiceProvider! applicationServices) -> void
Microsoft.AspNetCore.Routing.RouteOptions.SetParameterPolicy(string! token, System.Type! type) -> void
Microsoft.AspNetCore.Routing.RouteOptions.SetParameterPolicy<T>(string! token) -> void
override Microsoft.AspNetCore.Routing.CompositeEndpointDataSource.GetEndpointGroup(Microsoft.AspNetCore.Routing.RouteGroupContext! context) -> System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.Http.Endpoint!>!
override Microsoft.AspNetCore.Routing.CompositeEndpointDataSource.GetGroupedEndpoints(Microsoft.AspNetCore.Routing.RouteGroupContext! context) -> System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.Http.Endpoint!>!
static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapGroup(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, Microsoft.AspNetCore.Routing.Patterns.RoutePattern! prefix) -> Microsoft.AspNetCore.Routing.RouteGroupBuilder!
static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapGroup(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! prefix) -> Microsoft.AspNetCore.Routing.RouteGroupBuilder!
static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapPatch(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder!
static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapPatch(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, Microsoft.AspNetCore.Http.RequestDelegate! requestDelegate) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder!
override Microsoft.AspNetCore.Routing.RouteValuesAddress.ToString() -> string?
static Microsoft.AspNetCore.Http.EndpointFilterExtensions.AddEndpointFilter<TBuilder, TFilterType>(this TBuilder builder) -> TBuilder
static Microsoft.AspNetCore.Http.EndpointFilterExtensions.AddEndpointFilter<TBuilder>(this TBuilder builder, Microsoft.AspNetCore.Http.IEndpointFilter! filter) -> TBuilder
static Microsoft.AspNetCore.Http.EndpointFilterExtensions.AddEndpointFilter<TBuilder>(this TBuilder builder, System.Func<Microsoft.AspNetCore.Http.EndpointFilterFactoryContext!, Microsoft.AspNetCore.Http.EndpointFilterDelegate!, Microsoft.AspNetCore.Http.EndpointFilterDelegate!>! filterFactory) -> TBuilder
static Microsoft.AspNetCore.Http.EndpointFilterExtensions.AddEndpointFilter<TBuilder>(this TBuilder builder, System.Func<Microsoft.AspNetCore.Http.EndpointFilterInvocationContext!, Microsoft.AspNetCore.Http.EndpointFilterDelegate!, System.Threading.Tasks.ValueTask<object?>>! routeHandlerFilter) -> TBuilder
static Microsoft.AspNetCore.Http.EndpointFilterExtensions.AddEndpointFilter<TFilterType>(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder!
static Microsoft.AspNetCore.Http.EndpointFilterExtensions.AddEndpointFilter<TFilterType>(this Microsoft.AspNetCore.Routing.RouteGroupBuilder! builder) -> Microsoft.AspNetCore.Routing.RouteGroupBuilder!
static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.ExcludeFromDescription<TBuilder>(this TBuilder builder) -> TBuilder
static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.WithDescription<TBuilder>(this TBuilder builder, string! description) -> TBuilder
static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.WithSummary<TBuilder>(this TBuilder builder, string! summary) -> TBuilder
static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.WithTags<TBuilder>(this TBuilder builder, params string![]! tags) -> TBuilder
static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddRouteHandlerFilter<TBuilder, TFilterType>(this TBuilder builder) -> TBuilder
static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddRouteHandlerFilter<TBuilder>(this TBuilder builder, Microsoft.AspNetCore.Http.IRouteHandlerFilter! filter) -> TBuilder
static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddRouteHandlerFilter<TBuilder>(this TBuilder builder, System.Func<Microsoft.AspNetCore.Http.RouteHandlerContext!, Microsoft.AspNetCore.Http.RouteHandlerFilterDelegate!, Microsoft.AspNetCore.Http.RouteHandlerFilterDelegate!>! filterFactory) -> TBuilder
static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddRouteHandlerFilter<TBuilder>(this TBuilder builder, System.Func<Microsoft.AspNetCore.Http.RouteHandlerInvocationContext!, Microsoft.AspNetCore.Http.RouteHandlerFilterDelegate!, System.Threading.Tasks.ValueTask<object?>>! routeHandlerFilter) -> TBuilder
static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddRouteHandlerFilter<TFilterType>(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder!
static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddRouteHandlerFilter<TFilterType>(this Microsoft.AspNetCore.Routing.RouteGroupBuilder! builder) -> Microsoft.AspNetCore.Routing.RouteGroupBuilder!
static Microsoft.AspNetCore.Routing.LinkGeneratorEndpointNameAddressExtensions.GetPathByName(this Microsoft.AspNetCore.Routing.LinkGenerator! generator, Microsoft.AspNetCore.Http.HttpContext! httpContext, string! endpointName, Microsoft.AspNetCore.Routing.RouteValueDictionary? values = null, Microsoft.AspNetCore.Http.PathString? pathBase = null, Microsoft.AspNetCore.Http.FragmentString fragment = default(Microsoft.AspNetCore.Http.FragmentString), Microsoft.AspNetCore.Routing.LinkOptions? options = null) -> string?
static Microsoft.AspNetCore.Routing.LinkGeneratorEndpointNameAddressExtensions.GetPathByName(this Microsoft.AspNetCore.Routing.LinkGenerator! generator, string! endpointName, Microsoft.AspNetCore.Routing.RouteValueDictionary? values = null, Microsoft.AspNetCore.Http.PathString pathBase = default(Microsoft.AspNetCore.Http.PathString), Microsoft.AspNetCore.Http.FragmentString fragment = default(Microsoft.AspNetCore.Http.FragmentString), Microsoft.AspNetCore.Routing.LinkOptions? options = null) -> string?
static Microsoft.AspNetCore.Routing.LinkGeneratorEndpointNameAddressExtensions.GetUriByName(this Microsoft.AspNetCore.Routing.LinkGenerator! generator, Microsoft.AspNetCore.Http.HttpContext! httpContext, string! endpointName, Microsoft.AspNetCore.Routing.RouteValueDictionary? values = null, string? scheme = null, Microsoft.AspNetCore.Http.HostString? host = null, Microsoft.AspNetCore.Http.PathString? pathBase = null, Microsoft.AspNetCore.Http.FragmentString fragment = default(Microsoft.AspNetCore.Http.FragmentString), Microsoft.AspNetCore.Routing.LinkOptions? options = null) -> string?
Expand All @@ -39,5 +39,5 @@ static Microsoft.AspNetCore.Routing.Patterns.RoutePatternFactory.Pattern(Microso
static Microsoft.AspNetCore.Routing.Patterns.RoutePatternFactory.Pattern(Microsoft.AspNetCore.Routing.RouteValueDictionary? defaults, Microsoft.AspNetCore.Routing.RouteValueDictionary? parameterPolicies, params Microsoft.AspNetCore.Routing.Patterns.RoutePatternPathSegment![]! segments) -> Microsoft.AspNetCore.Routing.Patterns.RoutePattern!
static Microsoft.AspNetCore.Routing.Patterns.RoutePatternFactory.Pattern(string? rawText, Microsoft.AspNetCore.Routing.RouteValueDictionary? defaults, Microsoft.AspNetCore.Routing.RouteValueDictionary? parameterPolicies, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Routing.Patterns.RoutePatternPathSegment!>! segments) -> Microsoft.AspNetCore.Routing.Patterns.RoutePattern!
static Microsoft.AspNetCore.Routing.Patterns.RoutePatternFactory.Pattern(string? rawText, Microsoft.AspNetCore.Routing.RouteValueDictionary? defaults, Microsoft.AspNetCore.Routing.RouteValueDictionary? parameterPolicies, params Microsoft.AspNetCore.Routing.Patterns.RoutePatternPathSegment![]! segments) -> Microsoft.AspNetCore.Routing.Patterns.RoutePattern!
virtual Microsoft.AspNetCore.Routing.EndpointDataSource.GetEndpointGroup(Microsoft.AspNetCore.Routing.RouteGroupContext! context) -> System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.Http.Endpoint!>!
virtual Microsoft.AspNetCore.Routing.EndpointDataSource.GetGroupedEndpoints(Microsoft.AspNetCore.Routing.RouteGroupContext! context) -> System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.Http.Endpoint!>!
virtual Microsoft.AspNetCore.Routing.Patterns.RoutePatternTransformer.SubstituteRequiredValues(Microsoft.AspNetCore.Routing.Patterns.RoutePattern! original, Microsoft.AspNetCore.Routing.RouteValueDictionary! requiredValues) -> Microsoft.AspNetCore.Routing.Patterns.RoutePattern?
12 changes: 6 additions & 6 deletions src/Http/Routing/src/RouteEndpointBuilder.cs
Expand Up @@ -13,9 +13,9 @@ namespace Microsoft.AspNetCore.Routing;
/// </summary>
public sealed class RouteEndpointBuilder : EndpointBuilder
{
// TODO: Make this public as a gettable IReadOnlyList<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>.
// AddRouteHandlerFilter will still be the only way to mutate this list.
internal List<Func<RouteHandlerContext, RouteHandlerFilterDelegate, RouteHandlerFilterDelegate>>? RouteHandlerFilterFactories { get; set; }
// TODO: Make this public as a gettable IReadOnlyList<Func<RouteHandlerContext, EndpointFilterDelegate, EndpointFilterDelegate>>.
// AddEndpointFilter will still be the only way to mutate this list.
internal List<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>>? EndpointFilterFactories { get; set; }

/// <summary>
/// Gets or sets the <see cref="RoutePattern"/> associated with this endpoint.
Expand Down Expand Up @@ -45,7 +45,7 @@ public sealed class RouteEndpointBuilder : EndpointBuilder

/// <inheritdoc />
[UnconditionalSuppressMessage("Trimmer", "IL2026",
Justification = "We surface a RequireUnreferencedCode in AddRouteHandlerFilter which is required to call unreferenced code here. The trimmer is unable to infer this.")]
Justification = "We surface a RequireUnreferencedCode in AddEndpointFilter which is required to call unreferenced code here. The trimmer is unable to infer this.")]
public override Endpoint Build()
{
if (RequestDelegate is null)
Expand All @@ -60,14 +60,14 @@ public override Endpoint Build()
// ModelEndpointDataSource (Map(RoutePattern, RequestDelegate) and by extension MapHub, MapHealthChecks, etc...),
// ActionEndpointDataSourceBase (MapControllers, MapRazorPages, etc...) and people with custom data sources or otherwise manually building endpoints
// using this type. At the moment this class is sealed, so at the moment we do not need to concern ourselves with what derived types may be doing.
if (RouteHandlerFilterFactories is { Count: > 0 })
if (EndpointFilterFactories is { Count: > 0 })
{
// Even with filters applied, RDF.Create() will return back the exact same RequestDelegate instance we pass in if filters decide not to modify the
// invocation pipeline. We're just passing in a RequestDelegate so none of the fancy options pertaining to how the Delegate parameters are handled
// do not matter.
RequestDelegateFactoryOptions rdfOptions = new()
{
RouteHandlerFilterFactories = RouteHandlerFilterFactories,
EndpointFilterFactories = EndpointFilterFactories,
EndpointMetadata = Metadata,
};

Expand Down
6 changes: 3 additions & 3 deletions src/Http/Routing/src/RouteEndpointDataSource.cs
Expand Up @@ -58,7 +58,7 @@ public override IReadOnlyList<RouteEndpoint> Endpoints
}
}

public override IReadOnlyList<RouteEndpoint> GetEndpointGroup(RouteGroupContext context)
public override IReadOnlyList<RouteEndpoint> GetGroupedEndpoints(RouteGroupContext context)
{
var endpoints = new RouteEndpoint[_routeEntries.Count];
for (int i = 0; i < _routeEntries.Count; i++)
Expand Down Expand Up @@ -179,14 +179,14 @@ internal RouteEndpointBuilder GetSingleRouteEndpointBuilder()
ThrowOnBadRequest = _throwOnBadRequest,
DisableInferBodyFromParameters = ShouldDisableInferredBodyParameters(entry.HttpMethods),
EndpointMetadata = builder.Metadata,
RouteHandlerFilterFactories = builder.RouteHandlerFilterFactories,
EndpointFilterFactories = builder.EndpointFilterFactories,
};

// We ignore the returned EndpointMetadata has been already populated since we passed in non-null EndpointMetadata.
factoryCreatedRequestDelegate = RequestDelegateFactory.Create(entry.RouteHandler, factoryOptions).RequestDelegate;

// Clear out any filters so they don't get rerun in Build(). We can rethink how we do this later when exposed as public API.
builder.RouteHandlerFilterFactories = null;
builder.EndpointFilterFactories = null;

if (ReferenceEquals(builder.RequestDelegate, redirectedRequestDelegate))
{
Expand Down
6 changes: 3 additions & 3 deletions src/Http/Routing/src/RouteGroupBuilder.cs
Expand Up @@ -48,15 +48,15 @@ public GroupEndpointDataSource(RouteGroupBuilder groupRouteBuilder)
public override IReadOnlyList<Endpoint> Endpoints =>
GetGroupedEndpointsWithNullablePrefix(null, Array.Empty<Action<EndpointBuilder>>(), _routeGroupBuilder._outerEndpointRouteBuilder.ServiceProvider);

public override IReadOnlyList<Endpoint> GetEndpointGroup(RouteGroupContext context) =>
public override IReadOnlyList<Endpoint> GetGroupedEndpoints(RouteGroupContext context) =>
GetGroupedEndpointsWithNullablePrefix(context.Prefix, context.Conventions, context.ApplicationServices);

public IReadOnlyList<Endpoint> GetGroupedEndpointsWithNullablePrefix(RoutePattern? prefix, IReadOnlyList<Action<EndpointBuilder>> conventions, IServiceProvider applicationServices)
{
return _routeGroupBuilder._dataSources.Count switch
{
0 => Array.Empty<Endpoint>(),
1 => _routeGroupBuilder._dataSources[0].GetEndpointGroup(GetNextRouteGroupContext(prefix, conventions, applicationServices)),
1 => _routeGroupBuilder._dataSources[0].GetGroupedEndpoints(GetNextRouteGroupContext(prefix, conventions, applicationServices)),
_ => SelectEndpointsFromAllDataSources(GetNextRouteGroupContext(prefix, conventions, applicationServices)),
};
}
Expand Down Expand Up @@ -92,7 +92,7 @@ private IReadOnlyList<Endpoint> SelectEndpointsFromAllDataSources(RouteGroupCont

foreach (var dataSource in _routeGroupBuilder._dataSources)
{
groupedEndpoints.AddRange(dataSource.GetEndpointGroup(context));
groupedEndpoints.AddRange(dataSource.GetGroupedEndpoints(context));
}

return groupedEndpoints;
Expand Down
6 changes: 3 additions & 3 deletions src/Http/Routing/src/RouteGroupContext.cs
Expand Up @@ -7,7 +7,7 @@
namespace Microsoft.AspNetCore.Routing;

/// <summary>
/// Represents the information accessible to <see cref="EndpointDataSource.GetEndpointGroup(RouteGroupContext)"/>.
/// Represents the information accessible to <see cref="EndpointDataSource.GetGroupedEndpoints(RouteGroupContext)"/>.
/// </summary>
public sealed class RouteGroupContext
{
Expand All @@ -30,15 +30,15 @@ public RouteGroupContext(RoutePattern prefix, IReadOnlyList<Action<EndpointBuild

/// <summary>
/// Gets the <see cref="RouteEndpoint.RoutePattern"/> which should prefix the <see cref="RouteEndpoint.RoutePattern"/> of all <see cref="RouteEndpoint"/> instances
/// returned by the call to <see cref="EndpointDataSource.GetEndpointGroup(RouteGroupContext)"/>. This accounts for nested groups and gives the full group prefix
/// returned by the call to <see cref="EndpointDataSource.GetGroupedEndpoints(RouteGroupContext)"/>. This accounts for nested groups and gives the full group prefix
/// not just the prefix supplied to the innermost call to <see cref="EndpointRouteBuilderExtensions.MapGroup(IEndpointRouteBuilder, RoutePattern)"/>.
/// </summary>
public RoutePattern Prefix { get; }

/// <summary>
/// Gets all conventions added to ancestor <see cref="RouteGroupBuilder"/> instances returned from <see cref="EndpointRouteBuilderExtensions.MapGroup(IEndpointRouteBuilder, RoutePattern)"/>
/// via <see cref="IEndpointConventionBuilder.Add(Action{EndpointBuilder})"/>. These should be applied in order when building every <see cref="RouteEndpoint"/>
/// returned from <see cref="EndpointDataSource.GetEndpointGroup(RouteGroupContext)"/>.
/// returned from <see cref="EndpointDataSource.GetGroupedEndpoints(RouteGroupContext)"/>.
/// </summary>
public IReadOnlyList<Action<EndpointBuilder>> Conventions { get; }

Expand Down
Expand Up @@ -109,7 +109,7 @@ public async Task MapEndpoint_ReturnGenericTypeTask_GeneratedDelegate(Func<IEndp

[Theory]
[MemberData(nameof(MapMethods))]
public async Task MapEndpoint_CanBeFiltered_ByRouteHandlerFilters(Func<IEndpointRouteBuilder, string, RequestDelegate, IEndpointConventionBuilder> map)
public async Task MapEndpoint_CanBeFiltered_ByEndpointFilters(Func<IEndpointRouteBuilder, string, RequestDelegate, IEndpointConventionBuilder> map)
{
var builder = new DefaultEndpointRouteBuilder(new ApplicationBuilder(EmptyServiceProvider.Instance));
var httpContext = new DefaultHttpContext();
Expand All @@ -119,7 +119,7 @@ public async Task MapEndpoint_CanBeFiltered_ByRouteHandlerFilters(Func<IEndpoint
RequestDelegate initialRequestDelegate = static (context) => Task.CompletedTask;
var filterTag = new TagsAttribute("filter");

var endpointBuilder = map(builder, "/", initialRequestDelegate).AddRouteHandlerFilter(filterFactory: (routeHandlerContext, next) =>
var endpointBuilder = map(builder, "/", initialRequestDelegate).AddEndpointFilter(filterFactory: (routeHandlerContext, next) =>
{
routeHandlerContext.EndpointMetadata.Add(filterTag);
return async invocationContext =>
Expand Down Expand Up @@ -155,7 +155,7 @@ public void MapEndpoint_UsesOriginalRequestDelegateInstance_IfFilterDoesNotChang
RequestDelegate initialRequestDelegate = static (context) => Task.CompletedTask;
var filterTag = new TagsAttribute("filter");

var endpointBuilder = map(builder, "/", initialRequestDelegate).AddRouteHandlerFilter((routeHandlerContext, next) =>
var endpointBuilder = map(builder, "/", initialRequestDelegate).AddEndpointFilter((routeHandlerContext, next) =>
{
routeHandlerContext.EndpointMetadata.Add(filterTag);
return next;
Expand Down
Expand Up @@ -851,23 +851,23 @@ public async Task MapMethod_DefaultsToNotThrowOnBadHttpRequestIfItCannotResolveR

public static object[][] AddFiltersByClassData =
{
new object[] { (Action<IEndpointConventionBuilder>)((IEndpointConventionBuilder builder) => builder.AddRouteHandlerFilter(new IncrementArgFilter())) },
new object[] { (Action<IEndpointConventionBuilder>)((IEndpointConventionBuilder builder) => builder.AddRouteHandlerFilter<IEndpointConventionBuilder, IncrementArgFilter>()) }
new object[] { (Action<IEndpointConventionBuilder>)((IEndpointConventionBuilder builder) => builder.AddEndpointFilter(new IncrementArgFilter())) },
new object[] { (Action<IEndpointConventionBuilder>)((IEndpointConventionBuilder builder) => builder.AddEndpointFilter<IEndpointConventionBuilder, IncrementArgFilter>()) }
};

public static object[][] AddFiltersByDelegateData
{
get
{
void WithFilter(IEndpointConventionBuilder builder) =>
builder.AddRouteHandlerFilter(async (context, next) =>
builder.AddEndpointFilter(async (context, next) =>
{
context.Arguments[0] = ((int)context.Arguments[0]!) + 1;
return await next(context);
});

void WithFilterFactory(IEndpointConventionBuilder builder) =>
builder.AddRouteHandlerFilter((routeHandlerContext, next) => async (context) =>
builder.AddEndpointFilter((routeHandlerContext, next) => async (context) =>
{
Assert.NotNull(routeHandlerContext.MethodInfo);
Assert.NotNull(routeHandlerContext.MethodInfo.DeclaringType);
Expand Down Expand Up @@ -908,7 +908,7 @@ private static async Task AssertIdAsync(Endpoint endpoint, string expectedPatter
[Theory]
[MemberData(nameof(AddFiltersByClassData))]
[MemberData(nameof(AddFiltersByDelegateData))]
public async Task AddRouteHandlerFilterMethods_CanRegisterFilterWithClassAndDelegateImplementations(Action<IEndpointConventionBuilder> addFilter)
public async Task AddEndpointFilterMethods_CanRegisterFilterWithClassAndDelegateImplementations(Action<IEndpointConventionBuilder> addFilter)
{
var builder = new DefaultEndpointRouteBuilder(new ApplicationBuilder(new ServiceCollection().BuildServiceProvider()));

Expand All @@ -924,7 +924,7 @@ public async Task AddRouteHandlerFilterMethods_CanRegisterFilterWithClassAndDele
[Theory]
[MemberData(nameof(AddFiltersByClassData))]
[MemberData(nameof(AddFiltersByDelegateData))]
public async Task AddRouteHandlerFilterMethods_WorkWithMapGroup(Action<IEndpointConventionBuilder> addFilter)
public async Task AddEndpointFilterMethods_WorkWithMapGroup(Action<IEndpointConventionBuilder> addFilter)
{
var builder = new DefaultEndpointRouteBuilder(new ApplicationBuilder(new ServiceCollection().BuildServiceProvider()));

Expand Down Expand Up @@ -958,7 +958,7 @@ public async Task RequestDelegateFactory_CanInvokeEndpointFilter_ThatAccessesSer

string? PrintLogger(HttpContext context) => $"loggerErrorIsEnabled: {context.Items["loggerErrorIsEnabled"]}, parentName: {context.Items["parentName"]}";
var routeHandlerBuilder = builder.Map("/", PrintLogger);
routeHandlerBuilder.AddRouteHandlerFilter<ServiceAccessingRouteHandlerFilter>();
routeHandlerBuilder.AddEndpointFilter<ServiceAccessingEndpointFilter>();

var dataSource = GetBuilderEndpointDataSource(builder);
// Trigger Endpoint build by calling getter.
Expand Down Expand Up @@ -991,7 +991,7 @@ public void RequestDelegateFactory_ProvidesAppServiceProvider_ToFilterFactory()

string? PrintLogger(HttpContext context) => $"loggerErrorIsEnabled: {context.Items["loggerErrorIsEnabled"]}, parentName: {context.Items["parentName"]}";
var routeHandlerBuilder = builder.Map("/", PrintLogger);
routeHandlerBuilder.AddRouteHandlerFilter((rhc, next) =>
routeHandlerBuilder.AddEndpointFilter((rhc, next) =>
{
Assert.NotNull(rhc.ApplicationServices);
var myService = rhc.ApplicationServices.GetRequiredService<MyService>();
Expand All @@ -1009,47 +1009,47 @@ public void RequestDelegateFactory_ProvidesAppServiceProvider_ToFilterFactory()
[Fact]
public void RouteHandlerContext_ThrowsArgumentNullException_ForMethodInfo()
{
Assert.Throws<ArgumentNullException>("methodInfo", () => new RouteHandlerContext(null!, new List<object>(), new ServiceCollection().BuildServiceProvider()));
Assert.Throws<ArgumentNullException>("methodInfo", () => new EndpointFilterFactoryContext(null!, new List<object>(), new ServiceCollection().BuildServiceProvider()));
}

[Fact]
public void RouteHandlerContext_ThrowsArgumentNullException_ForEndpointMetadata()
{
var handler = () => { };
Assert.Throws<ArgumentNullException>("endpointMetadata", () => new RouteHandlerContext(handler.Method, null!, new ServiceCollection().BuildServiceProvider()));
Assert.Throws<ArgumentNullException>("endpointMetadata", () => new EndpointFilterFactoryContext(handler.Method, null!, new ServiceCollection().BuildServiceProvider()));
}

[Fact]
public void RouteHandlerContext_ThrowsArgumentNullException_ForApplicationServices()
{
var handler = () => { };
Assert.Throws<ArgumentNullException>("applicationServices", () => new RouteHandlerContext(handler.Method, new List<object>(), null!));
Assert.Throws<ArgumentNullException>("applicationServices", () => new EndpointFilterFactoryContext(handler.Method, new List<object>(), null!));
}

class MyService { }

class ServiceAccessingRouteHandlerFilter : IRouteHandlerFilter
class ServiceAccessingEndpointFilter : IEndpointFilter
{
private ILogger _logger;
private RouteHandlerContext _routeHandlerContext;
private EndpointFilterFactoryContext _routeHandlerContext;

public ServiceAccessingRouteHandlerFilter(ILoggerFactory loggerFactory, RouteHandlerContext routeHandlerContext)
public ServiceAccessingEndpointFilter(ILoggerFactory loggerFactory, EndpointFilterFactoryContext routeHandlerContext)
{
_logger = loggerFactory.CreateLogger<ServiceAccessingRouteHandlerFilter>();
_logger = loggerFactory.CreateLogger<ServiceAccessingEndpointFilter>();
_routeHandlerContext = routeHandlerContext;
}

public async ValueTask<object?> InvokeAsync(RouteHandlerInvocationContext context, RouteHandlerFilterDelegate next)
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
context.HttpContext.Items["loggerErrorIsEnabled"] = _logger.IsEnabled(LogLevel.Error);
context.HttpContext.Items["parentName"] = _routeHandlerContext.MethodInfo.DeclaringType?.Name;
return await next(context);
}
}

class IncrementArgFilter : IRouteHandlerFilter
class IncrementArgFilter : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(RouteHandlerInvocationContext context, RouteHandlerFilterDelegate next)
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
context.Arguments[0] = ((int)context.Arguments[0]!) + 1;
return await next(context);
Expand Down
Expand Up @@ -43,7 +43,7 @@ public void CreatesShallowCopyOf_ListOfGroupedEndpoints()
var conventions = Array.Empty<Action<EndpointBuilder>>();
var applicationServices = new ServiceCollection().BuildServiceProvider();

var groupedEndpoints = compositeDataSource.GetEndpointGroup(new RouteGroupContext(prefix, conventions, applicationServices));
var groupedEndpoints = compositeDataSource.GetGroupedEndpoints(new RouteGroupContext(prefix, conventions, applicationServices));

var resolvedGroupEndpoints = Assert.Single(dataSource.ResolvedGroupedEndpoints);
Assert.NotSame(groupedEndpoints, resolvedGroupEndpoints);
Expand Down Expand Up @@ -264,7 +264,7 @@ public void GetGroupedEndpoints_ForwardedToChildDataSources()
};

var context = new RouteGroupContext(prefix, conventions, applicationServices);
var groupedEndpoints = compositeDataSource.GetEndpointGroup(context);
var groupedEndpoints = compositeDataSource.GetGroupedEndpoints(context);

var receivedContext = Assert.Single(dataSource.ReceivedRouteGroupContexts);
Assert.Same(context, receivedContext);
Expand Down Expand Up @@ -299,10 +299,10 @@ private class TestGroupDataSource : EndpointDataSource

public List<IReadOnlyList<Endpoint>> ResolvedGroupedEndpoints { get; } = new();

public override IReadOnlyList<Endpoint> GetEndpointGroup(RouteGroupContext context)
public override IReadOnlyList<Endpoint> GetGroupedEndpoints(RouteGroupContext context)
{
ReceivedRouteGroupContexts.Add(context);
var resolved = base.GetEndpointGroup(context);
var resolved = base.GetGroupedEndpoints(context);
ResolvedGroupedEndpoints.Add(resolved);
return resolved;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Http/samples/MinimalSample/Program.cs
Expand Up @@ -21,7 +21,7 @@
var outer = app.MapGroup("/outer");
var inner = outer.MapGroup("/inner");

inner.AddRouteHandlerFilter((routeContext, next) =>
inner.AddEndpointFilter((routeContext, next) =>
{
var tags = routeContext.EndpointMetadata.OfType<ITagsMetadata>().FirstOrDefault();
Expand All @@ -36,7 +36,7 @@
outer.MapGet("/outerget", () => "I'm nested.");
inner.MapGet("/innerget", () => "I'm more nested.");

inner.AddRouteHandlerFilter((routeContext, next) =>
inner.AddEndpointFilter((routeContext, next) =>
{
Console.WriteLine($"Building filter! Num args: {routeContext.MethodInfo.GetParameters().Length}"); ;
return async invocationContext =>
Expand Down