From 07033c9aa17645ba5ecd34ba7c53baa79c1fe0a2 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Tue, 26 Apr 2022 13:57:10 -0700 Subject: [PATCH 1/6] Add support for RouteHandlerInvocationContext<> overloads --- ...rosoft.AspNetCore.Http.Abstractions.csproj | 20 + .../src/PublicAPI.Unshipped.txt | 5 +- .../src/RouteHandlerInvocationContext.cs | 24 +- .../src/RouteHandlerInvocationContextOfT.cs | 1409 +++++++++++++++++ .../src/RouteHandlerInvocationContextOfT.tt | 128 ++ .../RouteHandlerInvocationContextOfTTests.cs | 60 + .../src/RequestDelegateFactory.cs | 68 +- .../test/RequestDelegateFactoryTests.cs | 34 +- ...ndlerEndpointRouteBuilderExtensionsTest.cs | 2 +- 9 files changed, 1731 insertions(+), 19 deletions(-) create mode 100644 src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs create mode 100644 src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt create mode 100644 src/Http/Http.Abstractions/test/RouteHandlerInvocationContextOfTTests.cs diff --git a/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj b/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj index b8b80cd3f4a3..93c45fd612bf 100644 --- a/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj +++ b/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj @@ -30,6 +30,26 @@ Microsoft.AspNetCore.Http.HttpResponse + + + + + + TextTemplatingFileGenerator + RouteHandlerInvocationContextOfT.cs + + + + + + + + + + True + True + RouteHandlerInvocationContextOfT.tt + diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt index 20d456087930..57d499d58970 100644 --- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt @@ -14,8 +14,6 @@ Microsoft.AspNetCore.Http.RouteHandlerContext.MethodInfo.get -> System.Reflectio Microsoft.AspNetCore.Http.RouteHandlerContext.RouteHandlerContext(System.Reflection.MethodInfo! methodInfo, Microsoft.AspNetCore.Http.EndpointMetadataCollection! endpointMetadata) -> void Microsoft.AspNetCore.Http.RouteHandlerFilterDelegate Microsoft.AspNetCore.Http.RouteHandlerInvocationContext -Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.HttpContext.get -> Microsoft.AspNetCore.Http.HttpContext! -Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.Parameters.get -> System.Collections.Generic.IList! Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.RouteHandlerInvocationContext(Microsoft.AspNetCore.Http.HttpContext! httpContext, params object![]! parameters) -> void Microsoft.AspNetCore.Routing.RouteValueDictionary.RouteValueDictionary(Microsoft.AspNetCore.Routing.RouteValueDictionary? dictionary) -> void Microsoft.AspNetCore.Routing.RouteValueDictionary.RouteValueDictionary(System.Collections.Generic.IEnumerable>? values) -> void @@ -27,3 +25,6 @@ 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! +virtual Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.GetParameter(int index) -> T +virtual Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.HttpContext.get -> Microsoft.AspNetCore.Http.HttpContext! +virtual Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.Parameters.get -> System.Collections.Generic.IList! diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs index d7cfe600a760..6578a7f46b6b 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs @@ -7,8 +7,15 @@ namespace Microsoft.AspNetCore.Http; /// Provides an abstraction for wrapping the and parameters /// provided to a route handler. /// -public sealed class RouteHandlerInvocationContext +public class RouteHandlerInvocationContext { +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + // Provide an internal parameterless constructor for RHIC so that child classes + // can instantiate their instances without having to instantiate this base class + // and allocate an object[] + internal RouteHandlerInvocationContext() { } +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + /// /// Creates a new instance of the for a given request. /// @@ -23,7 +30,7 @@ public RouteHandlerInvocationContext(HttpContext httpContext, params object[] pa /// /// The associated with the current request being processed by the filter. /// - public HttpContext HttpContext { get; } + public virtual HttpContext HttpContext { get; } /// /// A list of parameters provided in the current request to the filter. @@ -31,5 +38,16 @@ public RouteHandlerInvocationContext(HttpContext httpContext, params object[] pa /// This list is not read-only to permit modifying of existing parameters by filters. /// /// - public IList Parameters { get; } + public virtual IList Parameters { get; } + + /// + /// Retrieve the parameter given its position in the argument list. + /// + /// The of the resolved parameter. + /// An integer representing the position of the parameter in the argument list. + /// The parameter at a given + public virtual T GetParameter(int index) + { + return (T)Parameters[index]!; + } } diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs new file mode 100644 index 000000000000..f739cfac8196 --- /dev/null +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs @@ -0,0 +1,1409 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +#nullable enable + +using System.Collections; + +namespace Microsoft.AspNetCore.Http; + +internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +{ + internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0) + { + HttpContext = httpContext; + Arg0 = arg0; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Parameters => this; + + public T0 Arg0 { get; set; } + + public int Count => 1; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Parameters.Count; i++) + { + array[arrayIndex++] = Parameters[i]; + } + } + + public IEnumerator GetEnumerator() + { + yield return Arg0; + + } + + public override T GetParameter(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Parameters.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + +} +internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +{ + internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object)value!; + break; + case 1: + Arg1 = (T1)(object)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Parameters => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + + public int Count => 2; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Parameters.Count; i++) + { + array[arrayIndex++] = Parameters[i]; + } + } + + public IEnumerator GetEnumerator() + { + yield return Arg0; + yield return Arg1; + + } + + public override T GetParameter(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Parameters.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + +} +internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +{ + internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object)value!; + break; + case 1: + Arg1 = (T1)(object)value!; + break; + case 2: + Arg2 = (T2)(object)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Parameters => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + + public int Count => 3; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Parameters.Count; i++) + { + array[arrayIndex++] = Parameters[i]; + } + } + + public IEnumerator GetEnumerator() + { + yield return Arg0; + yield return Arg1; + yield return Arg2; + + } + + public override T GetParameter(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Parameters.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + +} +internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +{ + internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object)value!; + break; + case 1: + Arg1 = (T1)(object)value!; + break; + case 2: + Arg2 = (T2)(object)value!; + break; + case 3: + Arg3 = (T3)(object)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Parameters => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + + public int Count => 4; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Parameters.Count; i++) + { + array[arrayIndex++] = Parameters[i]; + } + } + + public IEnumerator GetEnumerator() + { + yield return Arg0; + yield return Arg1; + yield return Arg2; + yield return Arg3; + + } + + public override T GetParameter(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Parameters.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + +} +internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +{ + internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object)value!; + break; + case 1: + Arg1 = (T1)(object)value!; + break; + case 2: + Arg2 = (T2)(object)value!; + break; + case 3: + Arg3 = (T3)(object)value!; + break; + case 4: + Arg4 = (T4)(object)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Parameters => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + + public int Count => 5; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Parameters.Count; i++) + { + array[arrayIndex++] = Parameters[i]; + } + } + + public IEnumerator GetEnumerator() + { + yield return Arg0; + yield return Arg1; + yield return Arg2; + yield return Arg3; + yield return Arg4; + + } + + public override T GetParameter(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Parameters.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + +} +internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +{ + internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object)value!; + break; + case 1: + Arg1 = (T1)(object)value!; + break; + case 2: + Arg2 = (T2)(object)value!; + break; + case 3: + Arg3 = (T3)(object)value!; + break; + case 4: + Arg4 = (T4)(object)value!; + break; + case 5: + Arg5 = (T5)(object)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Parameters => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + + public int Count => 6; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Parameters.Count; i++) + { + array[arrayIndex++] = Parameters[i]; + } + } + + public IEnumerator GetEnumerator() + { + yield return Arg0; + yield return Arg1; + yield return Arg2; + yield return Arg3; + yield return Arg4; + yield return Arg5; + + } + + public override T GetParameter(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Parameters.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + +} +internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +{ + internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object)value!; + break; + case 1: + Arg1 = (T1)(object)value!; + break; + case 2: + Arg2 = (T2)(object)value!; + break; + case 3: + Arg3 = (T3)(object)value!; + break; + case 4: + Arg4 = (T4)(object)value!; + break; + case 5: + Arg5 = (T5)(object)value!; + break; + case 6: + Arg6 = (T6)(object)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Parameters => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + + public int Count => 7; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Parameters.Count; i++) + { + array[arrayIndex++] = Parameters[i]; + } + } + + public IEnumerator GetEnumerator() + { + yield return Arg0; + yield return Arg1; + yield return Arg2; + yield return Arg3; + yield return Arg4; + yield return Arg5; + yield return Arg6; + + } + + public override T GetParameter(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Parameters.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + +} +internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +{ + internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + Arg7 = arg7; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + 7 => Arg7, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object)value!; + break; + case 1: + Arg1 = (T1)(object)value!; + break; + case 2: + Arg2 = (T2)(object)value!; + break; + case 3: + Arg3 = (T3)(object)value!; + break; + case 4: + Arg4 = (T4)(object)value!; + break; + case 5: + Arg5 = (T5)(object)value!; + break; + case 6: + Arg6 = (T6)(object)value!; + break; + case 7: + Arg7 = (T7)(object)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Parameters => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + public T7 Arg7 { get; set; } + + public int Count => 8; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Parameters.Count; i++) + { + array[arrayIndex++] = Parameters[i]; + } + } + + public IEnumerator GetEnumerator() + { + yield return Arg0; + yield return Arg1; + yield return Arg2; + yield return Arg3; + yield return Arg4; + yield return Arg5; + yield return Arg6; + yield return Arg7; + + } + + public override T GetParameter(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + 7 => (T)(object)Arg7!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Parameters.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + +} +internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +{ + internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + Arg7 = arg7; + Arg8 = arg8; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + 7 => Arg7, + 8 => Arg8, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object)value!; + break; + case 1: + Arg1 = (T1)(object)value!; + break; + case 2: + Arg2 = (T2)(object)value!; + break; + case 3: + Arg3 = (T3)(object)value!; + break; + case 4: + Arg4 = (T4)(object)value!; + break; + case 5: + Arg5 = (T5)(object)value!; + break; + case 6: + Arg6 = (T6)(object)value!; + break; + case 7: + Arg7 = (T7)(object)value!; + break; + case 8: + Arg8 = (T8)(object)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Parameters => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + public T7 Arg7 { get; set; } + public T8 Arg8 { get; set; } + + public int Count => 9; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Parameters.Count; i++) + { + array[arrayIndex++] = Parameters[i]; + } + } + + public IEnumerator GetEnumerator() + { + yield return Arg0; + yield return Arg1; + yield return Arg2; + yield return Arg3; + yield return Arg4; + yield return Arg5; + yield return Arg6; + yield return Arg7; + yield return Arg8; + + } + + public override T GetParameter(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + 7 => (T)(object)Arg7!, + 8 => (T)(object)Arg8!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Parameters.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + +} +internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +{ + internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + Arg7 = arg7; + Arg8 = arg8; + Arg9 = arg9; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + 7 => Arg7, + 8 => Arg8, + 9 => Arg9, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object)value!; + break; + case 1: + Arg1 = (T1)(object)value!; + break; + case 2: + Arg2 = (T2)(object)value!; + break; + case 3: + Arg3 = (T3)(object)value!; + break; + case 4: + Arg4 = (T4)(object)value!; + break; + case 5: + Arg5 = (T5)(object)value!; + break; + case 6: + Arg6 = (T6)(object)value!; + break; + case 7: + Arg7 = (T7)(object)value!; + break; + case 8: + Arg8 = (T8)(object)value!; + break; + case 9: + Arg9 = (T9)(object)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Parameters => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + public T7 Arg7 { get; set; } + public T8 Arg8 { get; set; } + public T9 Arg9 { get; set; } + + public int Count => 10; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Parameters.Count; i++) + { + array[arrayIndex++] = Parameters[i]; + } + } + + public IEnumerator GetEnumerator() + { + yield return Arg0; + yield return Arg1; + yield return Arg2; + yield return Arg3; + yield return Arg4; + yield return Arg5; + yield return Arg6; + yield return Arg7; + yield return Arg8; + yield return Arg9; + + } + + public override T GetParameter(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + 7 => (T)(object)Arg7!, + 8 => (T)(object)Arg8!, + 9 => (T)(object)Arg9!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Parameters.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + +} diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt new file mode 100644 index 000000000000..9ac35de677b5 --- /dev/null +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt @@ -0,0 +1,128 @@ +<#@ template language="C#" #> +<#@ output extension=".cs" #> +<#@ assembly name="System.Core.dll" #> +<#@ import namespace="System.Linq" #> +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +<# +int[] arities = Enumerable.Range(1, 10).ToArray(); +#> +#nullable enable + +using System.Collections; + +namespace Microsoft.AspNetCore.Http; + +<# foreach (var arity in arities) { #> +internal class RouteHandlerInvocationContext<<# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>T<#=argumentCount#><# if (argumentCount < arity - 1) { #>, <# } #><# } #>> : RouteHandlerInvocationContext, IList +{ + internal RouteHandlerInvocationContext(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#>; +<# } #> + } + + public object? this[int index] + { + get => index switch + { +<# foreach (var argumentCount in Enumerable.Range(0, arity)) { #> <#=argumentCount#> => Arg<#=argumentCount#>, +<# } #> + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { +<# foreach (var argumentCount in Enumerable.Range(0, arity)) { #> case <#=argumentCount#>: + Arg<#=argumentCount#> = (T<#=argumentCount#>)(object)value!; + break; +<# } #> + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Parameters => this; + + <# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>public T<#=argumentCount#> Arg<#=argumentCount#> { get; set; } + <# } #> + + public int Count => <#=arity#>; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Parameters.Count; i++) + { + array[arrayIndex++] = Parameters[i]; + } + } + + public IEnumerator GetEnumerator() + { +<# foreach (var argumentCount in Enumerable.Range(0, arity)) { #> yield return Arg<#=argumentCount#>; +<# } #> + + } + + public override T GetParameter(int index) + { + return index switch + { +<# foreach (var argumentCount in Enumerable.Range(0, arity)) { #> <#=argumentCount#> => (T)(object)Arg<#=argumentCount#>!, +<# } #> + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Parameters.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + +} +<# } #> diff --git a/src/Http/Http.Abstractions/test/RouteHandlerInvocationContextOfTTests.cs b/src/Http/Http.Abstractions/test/RouteHandlerInvocationContextOfTTests.cs new file mode 100644 index 000000000000..b7b9428d32ba --- /dev/null +++ b/src/Http/Http.Abstractions/test/RouteHandlerInvocationContextOfTTests.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http.Abstractions.Tests; + +public class RouteHandlerInvocationContextOfTTests +{ + [Fact] + public void ProhibitsActionsThatModifyListSize() + { + var context = new RouteHandlerInvocationContext(new DefaultHttpContext(), "This is a test", 42, false); + Assert.Throws(() => context.Add("string")); + Assert.Throws(() => context.Insert(0, "string")); + Assert.Throws(() => context.RemoveAt(0)); + Assert.Throws(() => context.Remove("string")); + Assert.Throws(() => context.Clear()); + } + + [Fact] + public void ThrowsExceptionForInvalidCastOnGetParameter() + { + var context = new RouteHandlerInvocationContext(new DefaultHttpContext(), "This is a test", 42, false, new Todo()); + Assert.Throws(() => context.GetParameter(1)); + Assert.Throws(() => context.GetParameter(0)); + Assert.Throws(() => context.GetParameter(3)); + var todo = context.GetParameter(3); + Assert.IsType(todo); + } + + [Fact] + public void SetterAllowsInPlaceModificationOfParameters() + { + var context = new RouteHandlerInvocationContext(new DefaultHttpContext(), "This is a test", 42, false, new Todo()); + context[0] = "Foo"; + Assert.Equal("Foo", context.GetParameter(0)); + } + + [Fact] + public void SetterDoesNotAllowModificationOfParameterType() + { + var context = new RouteHandlerInvocationContext(new DefaultHttpContext(), "This is a test", 42, false, new Todo()); + Assert.Throws(() => context[0] = 4); + } + + [Fact] + public void AllowsEnumerationOfParameters() + { + var context = new RouteHandlerInvocationContext(new DefaultHttpContext(), "This is a test", 42, false, new Todo()); + var enumeratedCount = 0; + foreach (var parameter in context) + { + Assert.NotNull(parameter); + enumeratedCount++; + } + Assert.Equal(4, enumeratedCount); + } + + interface ITodo { } + class Todo : ITodo { } +} diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index 4406099180a0..c2b5eae9e57a 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -95,8 +95,8 @@ public static partial class RequestDelegateFactory private static readonly UnaryExpression TempSourceStringIsNotNullOrEmptyExpr = Expression.Not(Expression.Call(StringIsNullOrEmptyMethod, TempSourceStringExpr)); private static readonly ConstructorInfo RouteHandlerInvocationContextConstructor = typeof(RouteHandlerInvocationContext).GetConstructor(new[] { typeof(HttpContext), typeof(object[]) })!; + private static readonly MethodInfo RouteHandlerInvocationContextGetParameter = typeof(RouteHandlerInvocationContext).GetMethod(nameof(RouteHandlerInvocationContext.GetParameter))!; private static readonly ParameterExpression FilterContextExpr = Expression.Parameter(typeof(RouteHandlerInvocationContext), "context"); - private static readonly MemberExpression FilterContextParametersExpr = Expression.Property(FilterContextExpr, typeof(RouteHandlerInvocationContext).GetProperty(nameof(RouteHandlerInvocationContext.Parameters))!); private static readonly MemberExpression FilterContextHttpContextExpr = Expression.Property(FilterContextExpr, typeof(RouteHandlerInvocationContext).GetProperty(nameof(RouteHandlerInvocationContext.HttpContext))!); 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))!); @@ -234,14 +234,13 @@ private static FactoryContext CreateFactoryContext(RequestDelegateFactoryOptions var filterPipeline = CreateFilterPipeline(methodInfo, targetExpression, factoryContext, targetFactory); Expression>> invokePipeline = (context) => filterPipeline(context); returnType = typeof(ValueTask); - // var filterContext = new RouteHandlerInvocationContext(httpContext, new[] { (object)name_local, (object)int_local }); + // var filterContext = new RouteHandlerInvocationContext(httpContext, name_local, int_local); // invokePipeline.Invoke(filterContext); factoryContext.MethodCall = Expression.Block( new[] { InvokedFilterContextExpr }, Expression.Assign( InvokedFilterContextExpr, - Expression.New(RouteHandlerInvocationContextConstructor, - new Expression[] { HttpContextExpr, Expression.NewArrayInit(typeof(object), factoryContext.BoxedArgs) })), + CreateRouteHandlerInvocationContext(factoryContext)), Expression.Invoke(invokePipeline, InvokedFilterContextExpr) ); } @@ -274,10 +273,10 @@ private static RouteHandlerFilterDelegate CreateFilterPipeline(MethodInfo method // When `handler` returns an object, we generate the following wrapper // to convert it to `ValueTask` as expected in the filter // pipeline. - // ValueTask.FromResult(handler((string)context.Parameters[0], (int)context.Parameters[1])); + // ValueTask.FromResult(handler(routeHandlerInvocationContext.GetParameter(0), routeHandlerInvocationContext.GetParameter(1))); // When the `handler` is a generic Task or ValueTask we await the task and // create a `ValueTask from the resulting value. - // new ValueTask(await handler((string)context.Parameters[0], (int)context.Parameters[1])); + // new ValueTask(await handler(routeHandlerInvocationContext.GetParameter(0), routeHandlerInvocationContext.GetParameter(1))); // When the `handler` returns a void or a void-returning Task, then we return an EmptyHttpResult // to as a ValueTask // } @@ -380,6 +379,50 @@ private static Expression MapHandlerReturnTypeToValueTask(Expression methodCall, return ExecuteAwaited(task); } + private static Expression CreateRouteHandlerInvocationContext(FactoryContext factoryContext) + { + // In the event that the a constructor matching the arity of the + // provided parameters is not found, we fall back to using the + // non-generic implementation of RouteHandlerInvocationContext. + var fallbackConstruction = Expression.New( + RouteHandlerInvocationContextConstructor, + new Expression[] { HttpContextExpr, Expression.NewArrayInit(typeof(object), factoryContext.BoxedArgs) }); + var types = new List() { typeof(HttpContext) }; + types.AddRange(factoryContext.ArgumentTypes); + var arguments = new List() { HttpContextExpr }; + arguments.AddRange(factoryContext.ArgumentExpressions); + var constructorType = factoryContext.ArgumentTypes.Count 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(RouteHandlerInvocationContext) + }; + + if (constructorType.IsGenericType) + { + var constructor = constructorType.MakeGenericType(factoryContext.ArgumentTypes.ToArray()).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).SingleOrDefault(); + if (constructor == null) + { + // new RouteHandlerInvocationContext(httpContext, (object)name_local, (object)int_local); + return fallbackConstruction; + } + + // new RouteHandlerInvocationContext(httpContext, name_local, int_local); + return Expression.New(constructor, arguments.ToArray()); + } + + // new RouteHandlerInvocationContext(httpContext, (object)name_local, (object)int_local); + return fallbackConstruction; + } + private static void AddTypeProvidedMetadata(MethodInfo methodInfo, List metadata, IServiceProvider? services) { object?[]? invokeArgs = null; @@ -462,12 +505,11 @@ private static Expression[] CreateArguments(ParameterInfo[]? parameters, Factory // Register expressions containing the boxed and unboxed variants // of the route handler's arguments for use in RouteHandlerInvocationContext // construction and route handler invocation. - // (string)context.Parameters[0]; - factoryContext.ContextArgAccess.Add( - Expression.Convert( - Expression.Property(FilterContextParametersExpr, "Item", Expression.Constant(i)), - parameters[i].ParameterType)); - // (object)name_local + // context.GetParameter(0) + factoryContext.ContextArgAccess.Add(Expression.Call(FilterContextExpr, RouteHandlerInvocationContextGetParameter.MakeGenericMethod(parameters[i].ParameterType), Expression.Constant(i))); + // (string, name_local), (int, int_local) + factoryContext.ArgumentTypes.Add(parameters[i].ParameterType); + factoryContext.ArgumentExpressions.Add(args[i]); factoryContext.BoxedArgs.Add(Expression.Convert(args[i], typeof(object))); } @@ -1951,6 +1993,8 @@ private class FactoryContext // Properties for constructing and managing filters public List ContextArgAccess { get; } = new(); public Expression? MethodCall { get; set; } + public List ArgumentTypes { get; } = new(); + public List ArgumentExpressions { get; } = new(); public List BoxedArgs { get; } = new(); public List>? Filters { get; init; } } diff --git a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs index 91bbe4907cc9..90ceed88ab2f 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs @@ -4685,7 +4685,8 @@ string HelloName(string name) }, (RouteHandlerContext, next) => async (context) => { - context.Parameters[0] = context.Parameters[0] != null ? $"{((string)context.Parameters[0]!)}Prefix" : "NULL"; + var newValue = $"{context.GetParameter(0)}Prefix"; + context.Parameters[0] = newValue; return await next(context); } } @@ -4983,6 +4984,37 @@ public async Task CanInvokeFilter_OnHandlerReturningTasksOfStruct(Delegate @dele Assert.Equal("Test todo", deserializedResponseBody.Name); } + [Fact] + public async Task RequestDelegateFactory_CanApplyFiltersOnHandlerWithManyArguments() + { + // Arrange + string HelloName(int? one, string? two, int? three, string? four, int? five, bool? six, string? seven, string? eight, int? nine, string? ten, int? eleven) + { + return "Too many arguments!"; + }; + + var httpContext = CreateHttpContext(); + + var responseBodyStream = new MemoryStream(); + httpContext.Response.Body = responseBodyStream; + + // Act + var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions() + { + RouteHandlerFilterFactories = new List>() + { + (routeHandlerContext, next) => async (context) => + { + Assert.IsType(context); + Assert.Equal(11, context.Parameters.Count); + return await next(context); + } + } + }); + var requestDelegate = factoryResult.RequestDelegate; + await requestDelegate(httpContext); + } + [Fact] public void Create_AddsDelegateMethodInfo_AsMetadata() { diff --git a/src/Http/Routing/test/UnitTests/Builder/RouteHandlerEndpointRouteBuilderExtensionsTest.cs b/src/Http/Routing/test/UnitTests/Builder/RouteHandlerEndpointRouteBuilderExtensionsTest.cs index 09e0e7e87392..5e37368001e6 100644 --- a/src/Http/Routing/test/UnitTests/Builder/RouteHandlerEndpointRouteBuilderExtensionsTest.cs +++ b/src/Http/Routing/test/UnitTests/Builder/RouteHandlerEndpointRouteBuilderExtensionsTest.cs @@ -902,7 +902,7 @@ void WithFilterFactory(RouteHandlerBuilder builder) => Assert.NotNull(routeHandlerContext.MethodInfo); Assert.NotNull(routeHandlerContext.MethodInfo.DeclaringType); Assert.Equal("RouteHandlerEndpointRouteBuilderExtensionsTest", routeHandlerContext.MethodInfo.DeclaringType?.Name); - context.Parameters[0] = ((int)context.Parameters[0]!) + 1; + context.Parameters[0] = context.GetParameter(0) + 1; return await next(context); }); From 033bc582d622d1f28407b2335cab8e6440d33741 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Sun, 1 May 2022 16:51:27 -0700 Subject: [PATCH 2/6] Address feedback from review --- .../src/RouteHandlerInvocationContextOfT.cs | 125 +++++++----------- .../src/RouteHandlerInvocationContextOfT.tt | 9 +- .../src/RequestDelegateFactory.cs | 31 +++-- .../test/RequestDelegateFactoryTests.cs | 31 +++++ 4 files changed, 104 insertions(+), 92 deletions(-) diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs index f739cfac8196..0139b3f6ce6a 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Http; -internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0) { @@ -72,8 +72,10 @@ public void CopyTo(object?[] array, int arrayIndex) public IEnumerator GetEnumerator() { - yield return Arg0; - + for (int i = 0; i < Parameters.Count; i++) + { + yield return Parameters[i]; + } } public override T GetParameter(int index) @@ -111,7 +113,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1) { @@ -182,9 +184,10 @@ public void CopyTo(object?[] array, int arrayIndex) public IEnumerator GetEnumerator() { - yield return Arg0; - yield return Arg1; - + for (int i = 0; i < Parameters.Count; i++) + { + yield return Parameters[i]; + } } public override T GetParameter(int index) @@ -223,7 +226,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2) { @@ -300,10 +303,10 @@ public void CopyTo(object?[] array, int arrayIndex) public IEnumerator GetEnumerator() { - yield return Arg0; - yield return Arg1; - yield return Arg2; - + for (int i = 0; i < Parameters.Count; i++) + { + yield return Parameters[i]; + } } public override T GetParameter(int index) @@ -343,7 +346,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3) { @@ -426,11 +429,10 @@ public void CopyTo(object?[] array, int arrayIndex) public IEnumerator GetEnumerator() { - yield return Arg0; - yield return Arg1; - yield return Arg2; - yield return Arg3; - + for (int i = 0; i < Parameters.Count; i++) + { + yield return Parameters[i]; + } } public override T GetParameter(int index) @@ -471,7 +473,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { @@ -560,12 +562,10 @@ public void CopyTo(object?[] array, int arrayIndex) public IEnumerator GetEnumerator() { - yield return Arg0; - yield return Arg1; - yield return Arg2; - yield return Arg3; - yield return Arg4; - + for (int i = 0; i < Parameters.Count; i++) + { + yield return Parameters[i]; + } } public override T GetParameter(int index) @@ -607,7 +607,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { @@ -702,13 +702,10 @@ public void CopyTo(object?[] array, int arrayIndex) public IEnumerator GetEnumerator() { - yield return Arg0; - yield return Arg1; - yield return Arg2; - yield return Arg3; - yield return Arg4; - yield return Arg5; - + for (int i = 0; i < Parameters.Count; i++) + { + yield return Parameters[i]; + } } public override T GetParameter(int index) @@ -751,7 +748,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { @@ -852,14 +849,10 @@ public void CopyTo(object?[] array, int arrayIndex) public IEnumerator GetEnumerator() { - yield return Arg0; - yield return Arg1; - yield return Arg2; - yield return Arg3; - yield return Arg4; - yield return Arg5; - yield return Arg6; - + for (int i = 0; i < Parameters.Count; i++) + { + yield return Parameters[i]; + } } public override T GetParameter(int index) @@ -903,7 +896,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) { @@ -1010,15 +1003,10 @@ public void CopyTo(object?[] array, int arrayIndex) public IEnumerator GetEnumerator() { - yield return Arg0; - yield return Arg1; - yield return Arg2; - yield return Arg3; - yield return Arg4; - yield return Arg5; - yield return Arg6; - yield return Arg7; - + for (int i = 0; i < Parameters.Count; i++) + { + yield return Parameters[i]; + } } public override T GetParameter(int index) @@ -1063,7 +1051,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) { @@ -1176,16 +1164,10 @@ public void CopyTo(object?[] array, int arrayIndex) public IEnumerator GetEnumerator() { - yield return Arg0; - yield return Arg1; - yield return Arg2; - yield return Arg3; - yield return Arg4; - yield return Arg5; - yield return Arg6; - yield return Arg7; - yield return Arg8; - + for (int i = 0; i < Parameters.Count; i++) + { + yield return Parameters[i]; + } } public override T GetParameter(int index) @@ -1231,7 +1213,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) { @@ -1350,17 +1332,10 @@ public void CopyTo(object?[] array, int arrayIndex) public IEnumerator GetEnumerator() { - yield return Arg0; - yield return Arg1; - yield return Arg2; - yield return Arg3; - yield return Arg4; - yield return Arg5; - yield return Arg6; - yield return Arg7; - yield return Arg8; - yield return Arg9; - + for (int i = 0; i < Parameters.Count; i++) + { + yield return Parameters[i]; + } } public override T GetParameter(int index) diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt index 9ac35de677b5..09e57ccd0077 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt @@ -15,7 +15,7 @@ using System.Collections; namespace Microsoft.AspNetCore.Http; <# foreach (var arity in arities) { #> -internal class RouteHandlerInvocationContext<<# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>T<#=argumentCount#><# if (argumentCount < arity - 1) { #>, <# } #><# } #>> : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext<<# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>T<#=argumentCount#><# if (argumentCount < arity - 1) { #>, <# } #><# } #>> : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, <# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>T<#=argumentCount#> arg<#=argumentCount#><# if (argumentCount < arity - 1) { #>, <# } #><# } #>) { @@ -84,9 +84,10 @@ internal class RouteHandlerInvocationContext<<# foreach (var argumentCount in En public IEnumerator GetEnumerator() { -<# foreach (var argumentCount in Enumerable.Range(0, arity)) { #> yield return Arg<#=argumentCount#>; -<# } #> - + for (int i = 0; i < Parameters.Count; i++) + { + yield return Parameters[i]; + } } public override T GetParameter(int index) diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index c2b5eae9e57a..88593e3368cc 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -387,11 +387,12 @@ private static Expression CreateRouteHandlerInvocationContext(FactoryContext fac var fallbackConstruction = Expression.New( RouteHandlerInvocationContextConstructor, new Expression[] { HttpContextExpr, Expression.NewArrayInit(typeof(object), factoryContext.BoxedArgs) }); - var types = new List() { typeof(HttpContext) }; - types.AddRange(factoryContext.ArgumentTypes); - var arguments = new List() { HttpContextExpr }; - arguments.AddRange(factoryContext.ArgumentExpressions); - var constructorType = factoryContext.ArgumentTypes.Count switch + + var arguments = new Expression[factoryContext.ArgumentExpressions.Length + 1]; + arguments[0] = HttpContextExpr; + factoryContext.ArgumentExpressions.CopyTo(arguments, 1); + + var constructorType = factoryContext.ArgumentTypes?.Length switch { 1 => typeof(RouteHandlerInvocationContext<>), 2 => typeof(RouteHandlerInvocationContext<,>), @@ -408,7 +409,7 @@ private static Expression CreateRouteHandlerInvocationContext(FactoryContext fac if (constructorType.IsGenericType) { - var constructor = constructorType.MakeGenericType(factoryContext.ArgumentTypes.ToArray()).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).SingleOrDefault(); + var constructor = constructorType.MakeGenericType(factoryContext.ArgumentTypes!).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).SingleOrDefault(); if (constructor == null) { // new RouteHandlerInvocationContext(httpContext, (object)name_local, (object)int_local); @@ -416,7 +417,7 @@ private static Expression CreateRouteHandlerInvocationContext(FactoryContext fac } // new RouteHandlerInvocationContext(httpContext, name_local, int_local); - return Expression.New(constructor, arguments.ToArray()); + return Expression.New(constructor, arguments); } // new RouteHandlerInvocationContext(httpContext, (object)name_local, (object)int_local); @@ -499,6 +500,10 @@ private static Expression[] CreateArguments(ParameterInfo[]? parameters, Factory var args = new Expression[parameters.Length]; + factoryContext.ArgumentTypes = new Type[parameters.Length]; + factoryContext.ArgumentExpressions = new Expression[parameters.Length]; + factoryContext.BoxedArgs = new Expression[parameters.Length]; + for (var i = 0; i < parameters.Length; i++) { args[i] = CreateArgument(parameters[i], factoryContext); @@ -508,9 +513,9 @@ private static Expression[] CreateArguments(ParameterInfo[]? parameters, Factory // context.GetParameter(0) factoryContext.ContextArgAccess.Add(Expression.Call(FilterContextExpr, RouteHandlerInvocationContextGetParameter.MakeGenericMethod(parameters[i].ParameterType), Expression.Constant(i))); // (string, name_local), (int, int_local) - factoryContext.ArgumentTypes.Add(parameters[i].ParameterType); - factoryContext.ArgumentExpressions.Add(args[i]); - factoryContext.BoxedArgs.Add(Expression.Convert(args[i], typeof(object))); + factoryContext.ArgumentTypes[i] = parameters[i].ParameterType; + factoryContext.ArgumentExpressions[i] = args[i]; + factoryContext.BoxedArgs[i] = Expression.Convert(args[i], typeof(object)); } if (factoryContext.HasInferredBody && factoryContext.DisableInferredFromBody) @@ -1993,9 +1998,9 @@ private class FactoryContext // Properties for constructing and managing filters public List ContextArgAccess { get; } = new(); public Expression? MethodCall { get; set; } - public List ArgumentTypes { get; } = new(); - public List ArgumentExpressions { get; } = new(); - public List BoxedArgs { get; } = new(); + public Type[] ArgumentTypes { get; set; } = Array.Empty(); + public Expression[] ArgumentExpressions { get; set; } = Array.Empty(); + public Expression[] BoxedArgs { get; set; } = Array.Empty(); public List>? Filters { get; init; } } diff --git a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs index 90ceed88ab2f..5dfd6b454018 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs @@ -5015,6 +5015,37 @@ string HelloName(int? one, string? two, int? three, string? four, int? five, boo await requestDelegate(httpContext); } + [Fact] + public async Task RequestDelegateFactory_CanApplyFiltersOnHandlerWithNoArguments() + { + // Arrange + string HelloName() + { + return "No arguments!"; + }; + + var httpContext = CreateHttpContext(); + + var responseBodyStream = new MemoryStream(); + httpContext.Response.Body = responseBodyStream; + + // Act + var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions() + { + RouteHandlerFilterFactories = new List>() + { + (routeHandlerContext, next) => async (context) => + { + Assert.IsType(context); + Assert.Equal(0, context.Parameters.Count); + return await next(context); + } + } + }); + var requestDelegate = factoryResult.RequestDelegate; + await requestDelegate(httpContext); + } + [Fact] public void Create_AddsDelegateMethodInfo_AsMetadata() { From 93a67263a35c6a2cb3f5e3af07642266fb5785f7 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Mon, 2 May 2022 17:52:01 -0700 Subject: [PATCH 3/6] Address feedback from API review --- .../src/IRouteHandlerFilter.cs | 6 +- .../src/PublicAPI.Unshipped.txt | 17 +- .../src/RouteHandlerFilterDelegate.cs | 4 +- .../src/RouteHandlerInvocationContext.cs | 45 ++--- .../src/RouteHandlerInvocationContextBase.cs | 32 ++++ .../src/RouteHandlerInvocationContextOfT.cs | 160 +++++++++--------- .../src/RouteHandlerInvocationContextOfT.tt | 16 +- .../RouteHandlerInvocationContextOfTTests.cs | 12 +- .../src/RequestDelegateFactory.cs | 42 ++--- .../test/RequestDelegateFactoryTests.cs | 24 +-- .../Builder/RouteHandlerFilterExtensions.cs | 2 +- src/Http/Routing/src/PublicAPI.Unshipped.txt | 2 +- ...ndlerEndpointRouteBuilderExtensionsTest.cs | 10 +- 13 files changed, 195 insertions(+), 177 deletions(-) create mode 100644 src/Http/Http.Abstractions/src/RouteHandlerInvocationContextBase.cs diff --git a/src/Http/Http.Abstractions/src/IRouteHandlerFilter.cs b/src/Http/Http.Abstractions/src/IRouteHandlerFilter.cs index 854947c4b351..4d057e43b5f2 100644 --- a/src/Http/Http.Abstractions/src/IRouteHandlerFilter.cs +++ b/src/Http/Http.Abstractions/src/IRouteHandlerFilter.cs @@ -9,12 +9,12 @@ namespace Microsoft.AspNetCore.Http; public interface IRouteHandlerFilter { /// - /// Implements the core logic associated with the filter given a + /// Implements the core logic associated with the filter given a /// and the next filter to call in the pipeline. /// - /// The associated with the current request/response. + /// The associated with the current request/response. /// The next filter in the pipeline. /// An awaitable result of calling the handler and apply /// any modifications made by filters in the pipeline. - ValueTask InvokeAsync(RouteHandlerInvocationContext context, RouteHandlerFilterDelegate next); + ValueTask InvokeAsync(RouteHandlerInvocationContextBase context, RouteHandlerFilterDelegate next); } diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt index 57d499d58970..671b18b20059 100644 --- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt @@ -3,9 +3,11 @@ *REMOVED*Microsoft.AspNetCore.Http.EndpointMetadataCollection.Enumerator.Current.get -> object? Microsoft.AspNetCore.Builder.EndpointBuilder.ServiceProvider.get -> System.IServiceProvider? Microsoft.AspNetCore.Builder.EndpointBuilder.ServiceProvider.set -> void +Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext +Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext.DefaultRouteHandlerInvocationContext(Microsoft.AspNetCore.Http.HttpContext! httpContext, params object![]! arguments) -> void Microsoft.AspNetCore.Http.EndpointMetadataCollection.Enumerator.Current.get -> object! Microsoft.AspNetCore.Http.EndpointMetadataCollection.GetRequiredMetadata() -> T! -Microsoft.AspNetCore.Http.IRouteHandlerFilter.InvokeAsync(Microsoft.AspNetCore.Http.RouteHandlerInvocationContext! context, Microsoft.AspNetCore.Http.RouteHandlerFilterDelegate! next) -> System.Threading.Tasks.ValueTask +Microsoft.AspNetCore.Http.IRouteHandlerFilter.InvokeAsync(Microsoft.AspNetCore.Http.RouteHandlerInvocationContextBase! context, Microsoft.AspNetCore.Http.RouteHandlerFilterDelegate! next) -> System.Threading.Tasks.ValueTask Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata.Name.get -> string? Microsoft.AspNetCore.Http.RouteHandlerContext @@ -13,8 +15,8 @@ Microsoft.AspNetCore.Http.RouteHandlerContext.EndpointMetadata.get -> Microsoft. 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.RouteHandlerFilterDelegate -Microsoft.AspNetCore.Http.RouteHandlerInvocationContext -Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.RouteHandlerInvocationContext(Microsoft.AspNetCore.Http.HttpContext! httpContext, params object![]! parameters) -> void +Microsoft.AspNetCore.Http.RouteHandlerInvocationContextBase +Microsoft.AspNetCore.Http.RouteHandlerInvocationContextBase.RouteHandlerInvocationContextBase() -> void Microsoft.AspNetCore.Routing.RouteValueDictionary.RouteValueDictionary(Microsoft.AspNetCore.Routing.RouteValueDictionary? dictionary) -> void Microsoft.AspNetCore.Routing.RouteValueDictionary.RouteValueDictionary(System.Collections.Generic.IEnumerable>? values) -> void Microsoft.AspNetCore.Routing.RouteValueDictionary.RouteValueDictionary(System.Collections.Generic.IEnumerable>? values) -> void @@ -25,6 +27,9 @@ 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! -virtual Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.GetParameter(int index) -> T -virtual Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.HttpContext.get -> Microsoft.AspNetCore.Http.HttpContext! -virtual Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.Parameters.get -> System.Collections.Generic.IList! +abstract Microsoft.AspNetCore.Http.RouteHandlerInvocationContextBase.Arguments.get -> System.Collections.Generic.IList! +abstract Microsoft.AspNetCore.Http.RouteHandlerInvocationContextBase.GetArgument(int index) -> T +abstract Microsoft.AspNetCore.Http.RouteHandlerInvocationContextBase.HttpContext.get -> Microsoft.AspNetCore.Http.HttpContext! +override Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext.Arguments.get -> System.Collections.Generic.IList! +override Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext.GetArgument(int index) -> T +override Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext.HttpContext.get -> Microsoft.AspNetCore.Http.HttpContext! diff --git a/src/Http/Http.Abstractions/src/RouteHandlerFilterDelegate.cs b/src/Http/Http.Abstractions/src/RouteHandlerFilterDelegate.cs index afc443a33a7d..bb9003e766cd 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerFilterDelegate.cs +++ b/src/Http/Http.Abstractions/src/RouteHandlerFilterDelegate.cs @@ -6,8 +6,8 @@ namespace Microsoft.AspNetCore.Http; /// /// A delegate that is applied as a filter on a route handler. /// -/// The associated with the current request. +/// The associated with the current request. /// /// A result of calling the handler and applying any modifications made by filters in the pipeline. /// -public delegate ValueTask RouteHandlerFilterDelegate(RouteHandlerInvocationContext context); +public delegate ValueTask RouteHandlerFilterDelegate(RouteHandlerInvocationContextBase context); diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs index 6578a7f46b6b..b1f9ac4d9fca 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs @@ -4,50 +4,31 @@ namespace Microsoft.AspNetCore.Http; /// -/// Provides an abstraction for wrapping the and parameters +/// Provides a default implementation for wrapping the and parameters /// provided to a route handler. /// -public class RouteHandlerInvocationContext +public class DefaultRouteHandlerInvocationContext : RouteHandlerInvocationContextBase { -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - // Provide an internal parameterless constructor for RHIC so that child classes - // can instantiate their instances without having to instantiate this base class - // and allocate an object[] - internal RouteHandlerInvocationContext() { } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - /// - /// Creates a new instance of the for a given request. + /// Creates a new instance of the for a given request. /// /// The associated with the current request. - /// A list of parameters provided in the current request. - public RouteHandlerInvocationContext(HttpContext httpContext, params object[] parameters) + /// A list of parameters provided in the current request. + public DefaultRouteHandlerInvocationContext(HttpContext httpContext, params object[] arguments) { HttpContext = httpContext; - Parameters = parameters; + Arguments = arguments; } - /// - /// The associated with the current request being processed by the filter. - /// - public virtual HttpContext HttpContext { get; } + /// + public override HttpContext HttpContext { get; } - /// - /// A list of parameters provided in the current request to the filter. - /// - /// This list is not read-only to permit modifying of existing parameters by filters. - /// - /// - public virtual IList Parameters { get; } + /// + public override IList Arguments { get; } - /// - /// Retrieve the parameter given its position in the argument list. - /// - /// The of the resolved parameter. - /// An integer representing the position of the parameter in the argument list. - /// The parameter at a given - public virtual T GetParameter(int index) + /// + public override T GetArgument(int index) { - return (T)Parameters[index]!; + return (T)Arguments[index]!; } } diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextBase.cs b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextBase.cs new file mode 100644 index 000000000000..5a108482b0b0 --- /dev/null +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextBase.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +/// +/// Provides an abstraction for wrapping the and arguments +/// provided to a route handler. +/// +public abstract class RouteHandlerInvocationContextBase +{ + /// + /// The associated with the current request being processed by the filter. + /// + public abstract HttpContext HttpContext { get; } + + /// + /// A list of arguments provided in the current request to the filter. + /// + /// This list is not read-only to permit modifying of existing arguments by filters. + /// + /// + public abstract IList Arguments { get; } + + /// + /// Retrieve the argument given its position in the argument list. + /// + /// The of the resolved argument. + /// An integer representing the position of the argument in the argument list. + /// The argument at a given + public abstract T GetArgument(int index); +} diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs index 0139b3f6ce6a..ff038220a834 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Http; -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0) { @@ -37,7 +37,7 @@ public object? this[int index] public override HttpContext HttpContext { get; } - public override IList Parameters => this; + public override IList Arguments => this; public T0 Arg0 { get; set; } @@ -64,21 +64,21 @@ public bool Contains(object? item) public void CopyTo(object?[] array, int arrayIndex) { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - array[arrayIndex++] = Parameters[i]; + array[arrayIndex++] = Arguments[i]; } } public IEnumerator GetEnumerator() { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - yield return Parameters[i]; + yield return Arguments[i]; } } - public override T GetParameter(int index) + public override T GetArgument(int index) { return index switch { @@ -89,7 +89,7 @@ public override T GetParameter(int index) public int IndexOf(object? item) { - return Parameters.IndexOf(item); + return Arguments.IndexOf(item); } public void Insert(int index, object? item) @@ -113,7 +113,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1) { @@ -148,7 +148,7 @@ public object? this[int index] public override HttpContext HttpContext { get; } - public override IList Parameters => this; + public override IList Arguments => this; public T0 Arg0 { get; set; } public T1 Arg1 { get; set; } @@ -176,21 +176,21 @@ public bool Contains(object? item) public void CopyTo(object?[] array, int arrayIndex) { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - array[arrayIndex++] = Parameters[i]; + array[arrayIndex++] = Arguments[i]; } } public IEnumerator GetEnumerator() { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - yield return Parameters[i]; + yield return Arguments[i]; } } - public override T GetParameter(int index) + public override T GetArgument(int index) { return index switch { @@ -202,7 +202,7 @@ public override T GetParameter(int index) public int IndexOf(object? item) { - return Parameters.IndexOf(item); + return Arguments.IndexOf(item); } public void Insert(int index, object? item) @@ -226,7 +226,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2) { @@ -266,7 +266,7 @@ public object? this[int index] public override HttpContext HttpContext { get; } - public override IList Parameters => this; + public override IList Arguments => this; public T0 Arg0 { get; set; } public T1 Arg1 { get; set; } @@ -295,21 +295,21 @@ public bool Contains(object? item) public void CopyTo(object?[] array, int arrayIndex) { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - array[arrayIndex++] = Parameters[i]; + array[arrayIndex++] = Arguments[i]; } } public IEnumerator GetEnumerator() { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - yield return Parameters[i]; + yield return Arguments[i]; } } - public override T GetParameter(int index) + public override T GetArgument(int index) { return index switch { @@ -322,7 +322,7 @@ public override T GetParameter(int index) public int IndexOf(object? item) { - return Parameters.IndexOf(item); + return Arguments.IndexOf(item); } public void Insert(int index, object? item) @@ -346,7 +346,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3) { @@ -391,7 +391,7 @@ public object? this[int index] public override HttpContext HttpContext { get; } - public override IList Parameters => this; + public override IList Arguments => this; public T0 Arg0 { get; set; } public T1 Arg1 { get; set; } @@ -421,21 +421,21 @@ public bool Contains(object? item) public void CopyTo(object?[] array, int arrayIndex) { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - array[arrayIndex++] = Parameters[i]; + array[arrayIndex++] = Arguments[i]; } } public IEnumerator GetEnumerator() { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - yield return Parameters[i]; + yield return Arguments[i]; } } - public override T GetParameter(int index) + public override T GetArgument(int index) { return index switch { @@ -449,7 +449,7 @@ public override T GetParameter(int index) public int IndexOf(object? item) { - return Parameters.IndexOf(item); + return Arguments.IndexOf(item); } public void Insert(int index, object? item) @@ -473,7 +473,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { @@ -523,7 +523,7 @@ public object? this[int index] public override HttpContext HttpContext { get; } - public override IList Parameters => this; + public override IList Arguments => this; public T0 Arg0 { get; set; } public T1 Arg1 { get; set; } @@ -554,21 +554,21 @@ public bool Contains(object? item) public void CopyTo(object?[] array, int arrayIndex) { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - array[arrayIndex++] = Parameters[i]; + array[arrayIndex++] = Arguments[i]; } } public IEnumerator GetEnumerator() { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - yield return Parameters[i]; + yield return Arguments[i]; } } - public override T GetParameter(int index) + public override T GetArgument(int index) { return index switch { @@ -583,7 +583,7 @@ public override T GetParameter(int index) public int IndexOf(object? item) { - return Parameters.IndexOf(item); + return Arguments.IndexOf(item); } public void Insert(int index, object? item) @@ -607,7 +607,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { @@ -662,7 +662,7 @@ public object? this[int index] public override HttpContext HttpContext { get; } - public override IList Parameters => this; + public override IList Arguments => this; public T0 Arg0 { get; set; } public T1 Arg1 { get; set; } @@ -694,21 +694,21 @@ public bool Contains(object? item) public void CopyTo(object?[] array, int arrayIndex) { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - array[arrayIndex++] = Parameters[i]; + array[arrayIndex++] = Arguments[i]; } } public IEnumerator GetEnumerator() { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - yield return Parameters[i]; + yield return Arguments[i]; } } - public override T GetParameter(int index) + public override T GetArgument(int index) { return index switch { @@ -724,7 +724,7 @@ public override T GetParameter(int index) public int IndexOf(object? item) { - return Parameters.IndexOf(item); + return Arguments.IndexOf(item); } public void Insert(int index, object? item) @@ -748,7 +748,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { @@ -808,7 +808,7 @@ public object? this[int index] public override HttpContext HttpContext { get; } - public override IList Parameters => this; + public override IList Arguments => this; public T0 Arg0 { get; set; } public T1 Arg1 { get; set; } @@ -841,21 +841,21 @@ public bool Contains(object? item) public void CopyTo(object?[] array, int arrayIndex) { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - array[arrayIndex++] = Parameters[i]; + array[arrayIndex++] = Arguments[i]; } } public IEnumerator GetEnumerator() { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - yield return Parameters[i]; + yield return Arguments[i]; } } - public override T GetParameter(int index) + public override T GetArgument(int index) { return index switch { @@ -872,7 +872,7 @@ public override T GetParameter(int index) public int IndexOf(object? item) { - return Parameters.IndexOf(item); + return Arguments.IndexOf(item); } public void Insert(int index, object? item) @@ -896,7 +896,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) { @@ -961,7 +961,7 @@ public object? this[int index] public override HttpContext HttpContext { get; } - public override IList Parameters => this; + public override IList Arguments => this; public T0 Arg0 { get; set; } public T1 Arg1 { get; set; } @@ -995,21 +995,21 @@ public bool Contains(object? item) public void CopyTo(object?[] array, int arrayIndex) { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - array[arrayIndex++] = Parameters[i]; + array[arrayIndex++] = Arguments[i]; } } public IEnumerator GetEnumerator() { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - yield return Parameters[i]; + yield return Arguments[i]; } } - public override T GetParameter(int index) + public override T GetArgument(int index) { return index switch { @@ -1027,7 +1027,7 @@ public override T GetParameter(int index) public int IndexOf(object? item) { - return Parameters.IndexOf(item); + return Arguments.IndexOf(item); } public void Insert(int index, object? item) @@ -1051,7 +1051,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) { @@ -1121,7 +1121,7 @@ public object? this[int index] public override HttpContext HttpContext { get; } - public override IList Parameters => this; + public override IList Arguments => this; public T0 Arg0 { get; set; } public T1 Arg1 { get; set; } @@ -1156,21 +1156,21 @@ public bool Contains(object? item) public void CopyTo(object?[] array, int arrayIndex) { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - array[arrayIndex++] = Parameters[i]; + array[arrayIndex++] = Arguments[i]; } } public IEnumerator GetEnumerator() { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - yield return Parameters[i]; + yield return Arguments[i]; } } - public override T GetParameter(int index) + public override T GetArgument(int index) { return index switch { @@ -1189,7 +1189,7 @@ public override T GetParameter(int index) public int IndexOf(object? item) { - return Parameters.IndexOf(item); + return Arguments.IndexOf(item); } public void Insert(int index, object? item) @@ -1213,7 +1213,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) { @@ -1288,7 +1288,7 @@ public object? this[int index] public override HttpContext HttpContext { get; } - public override IList Parameters => this; + public override IList Arguments => this; public T0 Arg0 { get; set; } public T1 Arg1 { get; set; } @@ -1324,21 +1324,21 @@ public bool Contains(object? item) public void CopyTo(object?[] array, int arrayIndex) { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - array[arrayIndex++] = Parameters[i]; + array[arrayIndex++] = Arguments[i]; } } public IEnumerator GetEnumerator() { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - yield return Parameters[i]; + yield return Arguments[i]; } } - public override T GetParameter(int index) + public override T GetArgument(int index) { return index switch { @@ -1358,7 +1358,7 @@ public override T GetParameter(int index) public int IndexOf(object? item) { - return Parameters.IndexOf(item); + return Arguments.IndexOf(item); } public void Insert(int index, object? item) diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt index 09e57ccd0077..a9050134ee95 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt @@ -15,7 +15,7 @@ using System.Collections; namespace Microsoft.AspNetCore.Http; <# foreach (var arity in arities) { #> -internal sealed class RouteHandlerInvocationContext<<# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>T<#=argumentCount#><# if (argumentCount < arity - 1) { #>, <# } #><# } #>> : RouteHandlerInvocationContext, IList +internal sealed class RouteHandlerInvocationContext<<# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>T<#=argumentCount#><# if (argumentCount < arity - 1) { #>, <# } #><# } #>> : RouteHandlerInvocationContextBase, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, <# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>T<#=argumentCount#> arg<#=argumentCount#><# if (argumentCount < arity - 1) { #>, <# } #><# } #>) { @@ -48,7 +48,7 @@ internal sealed class RouteHandlerInvocationContext<<# foreach (var argumentCoun public override HttpContext HttpContext { get; } - public override IList Parameters => this; + public override IList Arguments => this; <# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>public T<#=argumentCount#> Arg<#=argumentCount#> { get; set; } <# } #> @@ -76,21 +76,21 @@ internal sealed class RouteHandlerInvocationContext<<# foreach (var argumentCoun public void CopyTo(object?[] array, int arrayIndex) { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - array[arrayIndex++] = Parameters[i]; + array[arrayIndex++] = Arguments[i]; } } public IEnumerator GetEnumerator() { - for (int i = 0; i < Parameters.Count; i++) + for (int i = 0; i < Arguments.Count; i++) { - yield return Parameters[i]; + yield return Arguments[i]; } } - public override T GetParameter(int index) + public override T GetArgument(int index) { return index switch { @@ -102,7 +102,7 @@ internal sealed class RouteHandlerInvocationContext<<# foreach (var argumentCoun public int IndexOf(object? item) { - return Parameters.IndexOf(item); + return Arguments.IndexOf(item); } public void Insert(int index, object? item) diff --git a/src/Http/Http.Abstractions/test/RouteHandlerInvocationContextOfTTests.cs b/src/Http/Http.Abstractions/test/RouteHandlerInvocationContextOfTTests.cs index b7b9428d32ba..52f930c34312 100644 --- a/src/Http/Http.Abstractions/test/RouteHandlerInvocationContextOfTTests.cs +++ b/src/Http/Http.Abstractions/test/RouteHandlerInvocationContextOfTTests.cs @@ -17,13 +17,13 @@ public void ProhibitsActionsThatModifyListSize() } [Fact] - public void ThrowsExceptionForInvalidCastOnGetParameter() + public void ThrowsExceptionForInvalidCastOnGetArgument() { var context = new RouteHandlerInvocationContext(new DefaultHttpContext(), "This is a test", 42, false, new Todo()); - Assert.Throws(() => context.GetParameter(1)); - Assert.Throws(() => context.GetParameter(0)); - Assert.Throws(() => context.GetParameter(3)); - var todo = context.GetParameter(3); + Assert.Throws(() => context.GetArgument(1)); + Assert.Throws(() => context.GetArgument(0)); + Assert.Throws(() => context.GetArgument(3)); + var todo = context.GetArgument(3); Assert.IsType(todo); } @@ -32,7 +32,7 @@ public void SetterAllowsInPlaceModificationOfParameters() { var context = new RouteHandlerInvocationContext(new DefaultHttpContext(), "This is a test", 42, false, new Todo()); context[0] = "Foo"; - Assert.Equal("Foo", context.GetParameter(0)); + Assert.Equal("Foo", context.GetArgument(0)); } [Fact] diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index 88593e3368cc..3256efa6bc9f 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -94,13 +94,13 @@ 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 RouteHandlerInvocationContextConstructor = typeof(RouteHandlerInvocationContext).GetConstructor(new[] { typeof(HttpContext), typeof(object[]) })!; - private static readonly MethodInfo RouteHandlerInvocationContextGetParameter = typeof(RouteHandlerInvocationContext).GetMethod(nameof(RouteHandlerInvocationContext.GetParameter))!; - 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 ConstructorInfo DefaultRouteHandlerInvocationContextConstructor = typeof(DefaultRouteHandlerInvocationContext).GetConstructor(new[] { typeof(HttpContext), typeof(object[]) })!; + private static readonly MethodInfo RouteHandlerInvocationContextBaseGetArgument = typeof(RouteHandlerInvocationContextBase).GetMethod(nameof(RouteHandlerInvocationContextBase.GetArgument))!; + private static readonly ParameterExpression FilterContextExpr = Expression.Parameter(typeof(RouteHandlerInvocationContextBase), "context"); + private static readonly MemberExpression FilterContextHttpContextExpr = Expression.Property(FilterContextExpr, typeof(RouteHandlerInvocationContextBase).GetProperty(nameof(RouteHandlerInvocationContextBase.HttpContext))!); 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(RouteHandlerInvocationContextBase), "filterContext"); private static readonly string[] DefaultAcceptsContentType = new[] { "application/json" }; private static readonly string[] FormFileContentType = new[] { "multipart/form-data" }; @@ -232,15 +232,15 @@ private static FactoryContext CreateFactoryContext(RequestDelegateFactoryOptions if (factoryContext.Filters is { Count: > 0 }) { var filterPipeline = CreateFilterPipeline(methodInfo, targetExpression, factoryContext, targetFactory); - Expression>> invokePipeline = (context) => filterPipeline(context); + Expression>> invokePipeline = (context) => filterPipeline(context); returnType = typeof(ValueTask); - // var filterContext = new RouteHandlerInvocationContext(httpContext, name_local, int_local); + // var filterContext = new RouteHandlerInvocationContextBase(httpContext, name_local, int_local); // invokePipeline.Invoke(filterContext); factoryContext.MethodCall = Expression.Block( new[] { InvokedFilterContextExpr }, Expression.Assign( InvokedFilterContextExpr, - CreateRouteHandlerInvocationContext(factoryContext)), + CreateRouteHandlerInvocationContextBase(factoryContext)), Expression.Invoke(invokePipeline, InvokedFilterContextExpr) ); } @@ -273,10 +273,10 @@ private static RouteHandlerFilterDelegate CreateFilterPipeline(MethodInfo method // When `handler` returns an object, we generate the following wrapper // to convert it to `ValueTask` as expected in the filter // pipeline. - // ValueTask.FromResult(handler(routeHandlerInvocationContext.GetParameter(0), routeHandlerInvocationContext.GetParameter(1))); + // ValueTask.FromResult(handler(RouteHandlerInvocationContextBase.GetArgument(0), RouteHandlerInvocationContextBase.GetArgument(1))); // When the `handler` is a generic Task or ValueTask we await the task and // create a `ValueTask from the resulting value. - // new ValueTask(await handler(routeHandlerInvocationContext.GetParameter(0), routeHandlerInvocationContext.GetParameter(1))); + // new ValueTask(await handler(RouteHandlerInvocationContextBase.GetArgument(0), RouteHandlerInvocationContextBase.GetArgument(1))); // When the `handler` returns a void or a void-returning Task, then we return an EmptyHttpResult // to as a ValueTask // } @@ -307,7 +307,7 @@ targetExpression is null var currentFilterFactory = factoryContext.Filters[i]; var nextFilter = filteredInvocation; var currentFilter = currentFilterFactory(routeHandlerContext, nextFilter); - filteredInvocation = (RouteHandlerInvocationContext context) => currentFilter(context); + filteredInvocation = (RouteHandlerInvocationContextBase context) => currentFilter(context); } return filteredInvocation; @@ -379,13 +379,13 @@ private static Expression MapHandlerReturnTypeToValueTask(Expression methodCall, return ExecuteAwaited(task); } - private static Expression CreateRouteHandlerInvocationContext(FactoryContext factoryContext) + private static Expression CreateRouteHandlerInvocationContextBase(FactoryContext factoryContext) { // In the event that the 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 RouteHandlerInvocationContextBase. var fallbackConstruction = Expression.New( - RouteHandlerInvocationContextConstructor, + DefaultRouteHandlerInvocationContextConstructor, new Expression[] { HttpContextExpr, Expression.NewArrayInit(typeof(object), factoryContext.BoxedArgs) }); var arguments = new Expression[factoryContext.ArgumentExpressions.Length + 1]; @@ -404,7 +404,7 @@ private static Expression CreateRouteHandlerInvocationContext(FactoryContext fac 8 => typeof(RouteHandlerInvocationContext<,,,,,,,>), 9 => typeof(RouteHandlerInvocationContext<,,,,,,,,>), 10 => typeof(RouteHandlerInvocationContext<,,,,,,,,,>), - _ => typeof(RouteHandlerInvocationContext) + _ => typeof(DefaultRouteHandlerInvocationContext) }; if (constructorType.IsGenericType) @@ -412,15 +412,15 @@ private static Expression CreateRouteHandlerInvocationContext(FactoryContext fac 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 RouteHandlerInvocationContextBase(httpContext, (object)name_local, (object)int_local); return fallbackConstruction; } - // new RouteHandlerInvocationContext(httpContext, name_local, int_local); + // new RouteHandlerInvocationContextBase(httpContext, name_local, int_local); return Expression.New(constructor, arguments); } - // new RouteHandlerInvocationContext(httpContext, (object)name_local, (object)int_local); + // new RouteHandlerInvocationContextBase(httpContext, (object)name_local, (object)int_local); return fallbackConstruction; } @@ -508,10 +508,10 @@ private static Expression[] CreateArguments(ParameterInfo[]? parameters, Factory { args[i] = CreateArgument(parameters[i], factoryContext); // 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 RouteHandlerInvocationContextBase // construction and route handler invocation. - // context.GetParameter(0) - factoryContext.ContextArgAccess.Add(Expression.Call(FilterContextExpr, RouteHandlerInvocationContextGetParameter.MakeGenericMethod(parameters[i].ParameterType), Expression.Constant(i))); + // context.GetArgument(0) + factoryContext.ContextArgAccess.Add(Expression.Call(FilterContextExpr, RouteHandlerInvocationContextBaseGetArgument.MakeGenericMethod(parameters[i].ParameterType), Expression.Constant(i))); // (string, name_local), (int, int_local) factoryContext.ArgumentTypes[i] = parameters[i].ParameterType; factoryContext.ArgumentExpressions[i] = args[i]; diff --git a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs index 5dfd6b454018..bb070be027ba 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs @@ -4238,7 +4238,7 @@ string HelloName(string name) { (routeHandlerContext, next) => async (context) => { - context.Parameters[0] = context.Parameters[0] != null ? $"{((string)context.Parameters[0]!)}Prefix" : "NULL"; + context.Arguments[0] = context.Arguments[0] != null ? $"{((string)context.Arguments[0]!)}Prefix" : "NULL"; return await next(context); } } @@ -4438,12 +4438,12 @@ string HelloName(string name, int age) { (routeHandlerContext, next) => async (context) => { - context.Parameters[1] = ((int)context.Parameters[1]!) + 2; + context.Arguments[1] = ((int)context.Arguments[1]!) + 2; return await next(context); }, (routeHandlerContext, next) => async (context) => { - foreach (var parameter in context.Parameters) + foreach (var parameter in context.Arguments) { Log(parameter!.ToString() ?? "no arg"); } @@ -4492,7 +4492,7 @@ string HelloName(string name) { if (isInt) { - context.Parameters[1] = ((int)context.Parameters[1]!) + 2; + context.Arguments[1] = ((int)context.Arguments[1]!) + 2; return await next(context); } return "Is not an int."; @@ -4592,9 +4592,9 @@ string PrintTodo(Todo todo) { (routeHandlerContext, next) => async (context) => { - Todo originalTodo = (Todo)context.Parameters[0]!; + Todo originalTodo = (Todo)context.Arguments[0]!; originalTodo!.IsComplete = !originalTodo.IsComplete; - context.Parameters[0] = originalTodo; + context.Arguments[0] = originalTodo; return await next(context); } } @@ -4685,8 +4685,8 @@ string HelloName(string name) }, (RouteHandlerContext, next) => async (context) => { - var newValue = $"{context.GetParameter(0)}Prefix"; - context.Parameters[0] = newValue; + var newValue = $"{context.GetArgument(0)}Prefix"; + context.Arguments[0] = newValue; return await next(context); } } @@ -5005,8 +5005,8 @@ string HelloName(int? one, string? two, int? three, string? four, int? five, boo { (routeHandlerContext, next) => async (context) => { - Assert.IsType(context); - Assert.Equal(11, context.Parameters.Count); + Assert.IsType(context); + Assert.Equal(11, context.Arguments.Count); return await next(context); } } @@ -5036,8 +5036,8 @@ string HelloName() { (routeHandlerContext, next) => async (context) => { - Assert.IsType(context); - Assert.Equal(0, context.Parameters.Count); + Assert.IsType(context); + Assert.Equal(0, context.Arguments.Count); return await next(context); } } diff --git a/src/Http/Routing/src/Builder/RouteHandlerFilterExtensions.cs b/src/Http/Routing/src/Builder/RouteHandlerFilterExtensions.cs index f0032cc2bf73..ca5b618077e3 100644 --- a/src/Http/Routing/src/Builder/RouteHandlerFilterExtensions.cs +++ b/src/Http/Routing/src/Builder/RouteHandlerFilterExtensions.cs @@ -62,7 +62,7 @@ public static RouteHandlerBuilder AddFilter(this RouteHandlerBuilder builder, IR /// The . /// A representing the core logic of the filter. /// A that can be used to further customize the route handler. - public static RouteHandlerBuilder AddFilter(this RouteHandlerBuilder builder, Func> routeHandlerFilter) + public static RouteHandlerBuilder AddFilter(this RouteHandlerBuilder builder, Func> routeHandlerFilter) { builder.RouteHandlerFilterFactories.Add((routeHandlerContext, next) => (context) => routeHandlerFilter(context, next)); return builder; diff --git a/src/Http/Routing/src/PublicAPI.Unshipped.txt b/src/Http/Routing/src/PublicAPI.Unshipped.txt index 632d25fbd8af..c617932c4875 100644 --- a/src/Http/Routing/src/PublicAPI.Unshipped.txt +++ b/src/Http/Routing/src/PublicAPI.Unshipped.txt @@ -13,7 +13,7 @@ override Microsoft.AspNetCore.Routing.RouteValuesAddress.ToString() -> string? Microsoft.AspNetCore.Routing.DefaultInlineConstraintResolver.DefaultInlineConstraintResolver(Microsoft.Extensions.Options.IOptions! routeOptions, System.IServiceProvider! serviceProvider) -> void static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddFilter(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, Microsoft.AspNetCore.Http.IRouteHandlerFilter! filter) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddFilter(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, System.Func! filterFactory) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! -static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddFilter(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, System.Func>! routeHandlerFilter) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddFilter(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, System.Func>! routeHandlerFilter) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddFilter(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.WithDescription(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, string! description) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.WithSummary(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, string! summary) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! diff --git a/src/Http/Routing/test/UnitTests/Builder/RouteHandlerEndpointRouteBuilderExtensionsTest.cs b/src/Http/Routing/test/UnitTests/Builder/RouteHandlerEndpointRouteBuilderExtensionsTest.cs index 5e37368001e6..d5b9eb9dd420 100644 --- a/src/Http/Routing/test/UnitTests/Builder/RouteHandlerEndpointRouteBuilderExtensionsTest.cs +++ b/src/Http/Routing/test/UnitTests/Builder/RouteHandlerEndpointRouteBuilderExtensionsTest.cs @@ -892,7 +892,7 @@ public static object[][] AddFiltersByDelegateData void WithFilter(RouteHandlerBuilder builder) => builder.AddFilter(async (context, next) => { - context.Parameters[0] = ((int)context.Parameters[0]!) + 1; + context.Arguments[0] = ((int)context.Arguments[0]!) + 1; return await next(context); }); @@ -902,7 +902,7 @@ void WithFilterFactory(RouteHandlerBuilder builder) => Assert.NotNull(routeHandlerContext.MethodInfo); Assert.NotNull(routeHandlerContext.MethodInfo.DeclaringType); Assert.Equal("RouteHandlerEndpointRouteBuilderExtensionsTest", routeHandlerContext.MethodInfo.DeclaringType?.Name); - context.Parameters[0] = context.GetParameter(0) + 1; + context.Arguments[0] = context.GetArgument(0) + 1; return await next(context); }); @@ -983,7 +983,7 @@ public ServiceAccessingRouteHandlerFilter(ILoggerFactory loggerFactory, RouteHan _routeHandlerContext = routeHandlerContext; } - public async ValueTask InvokeAsync(RouteHandlerInvocationContext context, RouteHandlerFilterDelegate next) + public async ValueTask InvokeAsync(RouteHandlerInvocationContextBase context, RouteHandlerFilterDelegate next) { context.HttpContext.Items["loggerErrorIsEnabled"] = _logger.IsEnabled(LogLevel.Error); context.HttpContext.Items["parentName"] = _routeHandlerContext.MethodInfo.DeclaringType?.Name; @@ -993,9 +993,9 @@ public ServiceAccessingRouteHandlerFilter(ILoggerFactory loggerFactory, RouteHan class IncrementArgFilter : IRouteHandlerFilter { - public async ValueTask InvokeAsync(RouteHandlerInvocationContext context, RouteHandlerFilterDelegate next) + public async ValueTask InvokeAsync(RouteHandlerInvocationContextBase context, RouteHandlerFilterDelegate next) { - context.Parameters[0] = ((int)context.Parameters[0]!) + 1; + context.Arguments[0] = ((int)context.Arguments[0]!) + 1; return await next(context); } } From 5dd971f7dbb5c0c4cd840a0a597e67f8becc4875 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Mon, 2 May 2022 18:57:26 -0700 Subject: [PATCH 4/6] Rename RouteHandlerInvocationContextBase per API review --- .../DefaultRouteHandlerInvocationContext.cs | 34 +++++ .../src/IRouteHandlerFilter.cs | 6 +- .../src/PublicAPI.Unshipped.txt | 12 +- .../src/RouteHandlerFilterDelegate.cs | 4 +- .../src/RouteHandlerInvocationContext.cs | 38 +++-- .../src/RouteHandlerInvocationContextBase.cs | 32 ----- .../src/RouteHandlerInvocationContextOfT.cs | 130 +++++++++--------- .../src/RouteHandlerInvocationContextOfT.tt | 2 +- .../src/RequestDelegateFactory.cs | 30 ++-- .../Builder/RouteHandlerFilterExtensions.cs | 2 +- src/Http/Routing/src/PublicAPI.Unshipped.txt | 2 +- ...ndlerEndpointRouteBuilderExtensionsTest.cs | 4 +- 12 files changed, 148 insertions(+), 148 deletions(-) create mode 100644 src/Http/Http.Abstractions/src/DefaultRouteHandlerInvocationContext.cs delete mode 100644 src/Http/Http.Abstractions/src/RouteHandlerInvocationContextBase.cs diff --git a/src/Http/Http.Abstractions/src/DefaultRouteHandlerInvocationContext.cs b/src/Http/Http.Abstractions/src/DefaultRouteHandlerInvocationContext.cs new file mode 100644 index 000000000000..daa2096321c5 --- /dev/null +++ b/src/Http/Http.Abstractions/src/DefaultRouteHandlerInvocationContext.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http; + +/// +/// Provides a default implementation for wrapping the and parameters +/// provided to a route handler. +/// +public class DefaultRouteHandlerInvocationContext : RouteHandlerInvocationContext +{ + /// + /// Creates a new instance of the for a given request. + /// + /// The associated with the current request. + /// A list of parameters provided in the current request. + public DefaultRouteHandlerInvocationContext(HttpContext httpContext, params object[] arguments) + { + HttpContext = httpContext; + Arguments = arguments; + } + + /// + public override HttpContext HttpContext { get; } + + /// + public override IList Arguments { get; } + + /// + public override T GetArgument(int index) + { + return (T)Arguments[index]!; + } +} diff --git a/src/Http/Http.Abstractions/src/IRouteHandlerFilter.cs b/src/Http/Http.Abstractions/src/IRouteHandlerFilter.cs index 4d057e43b5f2..854947c4b351 100644 --- a/src/Http/Http.Abstractions/src/IRouteHandlerFilter.cs +++ b/src/Http/Http.Abstractions/src/IRouteHandlerFilter.cs @@ -9,12 +9,12 @@ namespace Microsoft.AspNetCore.Http; public interface IRouteHandlerFilter { /// - /// Implements the core logic associated with the filter given a + /// Implements the core logic associated with the filter given a /// and the next filter to call in the pipeline. /// - /// The associated with the current request/response. + /// The associated with the current request/response. /// The next filter in the pipeline. /// An awaitable result of calling the handler and apply /// any modifications made by filters in the pipeline. - ValueTask InvokeAsync(RouteHandlerInvocationContextBase context, RouteHandlerFilterDelegate next); + ValueTask InvokeAsync(RouteHandlerInvocationContext context, RouteHandlerFilterDelegate next); } diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt index 671b18b20059..54c1f4be51fc 100644 --- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt @@ -7,7 +7,7 @@ Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext.DefaultRouteHandlerInvocationContext(Microsoft.AspNetCore.Http.HttpContext! httpContext, params object![]! arguments) -> void Microsoft.AspNetCore.Http.EndpointMetadataCollection.Enumerator.Current.get -> object! Microsoft.AspNetCore.Http.EndpointMetadataCollection.GetRequiredMetadata() -> T! -Microsoft.AspNetCore.Http.IRouteHandlerFilter.InvokeAsync(Microsoft.AspNetCore.Http.RouteHandlerInvocationContextBase! context, Microsoft.AspNetCore.Http.RouteHandlerFilterDelegate! next) -> System.Threading.Tasks.ValueTask +Microsoft.AspNetCore.Http.IRouteHandlerFilter.InvokeAsync(Microsoft.AspNetCore.Http.RouteHandlerInvocationContext! context, Microsoft.AspNetCore.Http.RouteHandlerFilterDelegate! next) -> System.Threading.Tasks.ValueTask Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata.Name.get -> string? Microsoft.AspNetCore.Http.RouteHandlerContext @@ -15,8 +15,8 @@ Microsoft.AspNetCore.Http.RouteHandlerContext.EndpointMetadata.get -> Microsoft. 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.RouteHandlerFilterDelegate -Microsoft.AspNetCore.Http.RouteHandlerInvocationContextBase -Microsoft.AspNetCore.Http.RouteHandlerInvocationContextBase.RouteHandlerInvocationContextBase() -> void +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>? values) -> void Microsoft.AspNetCore.Routing.RouteValueDictionary.RouteValueDictionary(System.Collections.Generic.IEnumerable>? values) -> void @@ -27,9 +27,9 @@ 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.RouteHandlerInvocationContextBase.Arguments.get -> System.Collections.Generic.IList! -abstract Microsoft.AspNetCore.Http.RouteHandlerInvocationContextBase.GetArgument(int index) -> T -abstract Microsoft.AspNetCore.Http.RouteHandlerInvocationContextBase.HttpContext.get -> Microsoft.AspNetCore.Http.HttpContext! +abstract Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.Arguments.get -> System.Collections.Generic.IList! +abstract Microsoft.AspNetCore.Http.RouteHandlerInvocationContext.GetArgument(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! override Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext.GetArgument(int index) -> T override Microsoft.AspNetCore.Http.DefaultRouteHandlerInvocationContext.HttpContext.get -> Microsoft.AspNetCore.Http.HttpContext! diff --git a/src/Http/Http.Abstractions/src/RouteHandlerFilterDelegate.cs b/src/Http/Http.Abstractions/src/RouteHandlerFilterDelegate.cs index bb9003e766cd..afc443a33a7d 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerFilterDelegate.cs +++ b/src/Http/Http.Abstractions/src/RouteHandlerFilterDelegate.cs @@ -6,8 +6,8 @@ namespace Microsoft.AspNetCore.Http; /// /// A delegate that is applied as a filter on a route handler. /// -/// The associated with the current request. +/// The associated with the current request. /// /// A result of calling the handler and applying any modifications made by filters in the pipeline. /// -public delegate ValueTask RouteHandlerFilterDelegate(RouteHandlerInvocationContextBase context); +public delegate ValueTask RouteHandlerFilterDelegate(RouteHandlerInvocationContext context); diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs index b1f9ac4d9fca..4ae318236593 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs @@ -4,31 +4,29 @@ namespace Microsoft.AspNetCore.Http; /// -/// Provides a default implementation for wrapping the and parameters +/// Provides an abstraction for wrapping the and arguments /// provided to a route handler. /// -public class DefaultRouteHandlerInvocationContext : RouteHandlerInvocationContextBase +public abstract class RouteHandlerInvocationContext { /// - /// Creates a new instance of the for a given request. + /// The associated with the current request being processed by the filter. /// - /// The associated with the current request. - /// A list of parameters provided in the current request. - public DefaultRouteHandlerInvocationContext(HttpContext httpContext, params object[] arguments) - { - HttpContext = httpContext; - Arguments = arguments; - } + public abstract HttpContext HttpContext { get; } - /// - public override HttpContext HttpContext { get; } - - /// - public override IList Arguments { get; } + /// + /// A list of arguments provided in the current request to the filter. + /// + /// This list is not read-only to permit modifying of existing arguments by filters. + /// + /// + public abstract IList Arguments { get; } - /// - public override T GetArgument(int index) - { - return (T)Arguments[index]!; - } + /// + /// Retrieve the argument given its position in the argument list. + /// + /// The of the resolved argument. + /// An integer representing the position of the argument in the argument list. + /// The argument at a given + public abstract T GetArgument(int index); } diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextBase.cs b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextBase.cs deleted file mode 100644 index 5a108482b0b0..000000000000 --- a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextBase.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Http; - -/// -/// Provides an abstraction for wrapping the and arguments -/// provided to a route handler. -/// -public abstract class RouteHandlerInvocationContextBase -{ - /// - /// The associated with the current request being processed by the filter. - /// - public abstract HttpContext HttpContext { get; } - - /// - /// A list of arguments provided in the current request to the filter. - /// - /// This list is not read-only to permit modifying of existing arguments by filters. - /// - /// - public abstract IList Arguments { get; } - - /// - /// Retrieve the argument given its position in the argument list. - /// - /// The of the resolved argument. - /// An integer representing the position of the argument in the argument list. - /// The argument at a given - public abstract T GetArgument(int index); -} diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs index ff038220a834..cbd1cfce3f29 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Http; -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0) { @@ -27,7 +27,7 @@ public object? this[int index] switch (index) { case 0: - Arg0 = (T0)(object)value!; + Arg0 = (T0)(object?)value!; break; default: break; @@ -113,7 +113,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1) { @@ -135,10 +135,10 @@ public object? this[int index] switch (index) { case 0: - Arg0 = (T0)(object)value!; + Arg0 = (T0)(object?)value!; break; case 1: - Arg1 = (T1)(object)value!; + Arg1 = (T1)(object?)value!; break; default: break; @@ -226,7 +226,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2) { @@ -250,13 +250,13 @@ public object? this[int index] switch (index) { case 0: - Arg0 = (T0)(object)value!; + Arg0 = (T0)(object?)value!; break; case 1: - Arg1 = (T1)(object)value!; + Arg1 = (T1)(object?)value!; break; case 2: - Arg2 = (T2)(object)value!; + Arg2 = (T2)(object?)value!; break; default: break; @@ -346,7 +346,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3) { @@ -372,16 +372,16 @@ public object? this[int index] switch (index) { case 0: - Arg0 = (T0)(object)value!; + Arg0 = (T0)(object?)value!; break; case 1: - Arg1 = (T1)(object)value!; + Arg1 = (T1)(object?)value!; break; case 2: - Arg2 = (T2)(object)value!; + Arg2 = (T2)(object?)value!; break; case 3: - Arg3 = (T3)(object)value!; + Arg3 = (T3)(object?)value!; break; default: break; @@ -473,7 +473,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { @@ -501,19 +501,19 @@ public object? this[int index] switch (index) { case 0: - Arg0 = (T0)(object)value!; + Arg0 = (T0)(object?)value!; break; case 1: - Arg1 = (T1)(object)value!; + Arg1 = (T1)(object?)value!; break; case 2: - Arg2 = (T2)(object)value!; + Arg2 = (T2)(object?)value!; break; case 3: - Arg3 = (T3)(object)value!; + Arg3 = (T3)(object?)value!; break; case 4: - Arg4 = (T4)(object)value!; + Arg4 = (T4)(object?)value!; break; default: break; @@ -607,7 +607,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { @@ -637,22 +637,22 @@ public object? this[int index] switch (index) { case 0: - Arg0 = (T0)(object)value!; + Arg0 = (T0)(object?)value!; break; case 1: - Arg1 = (T1)(object)value!; + Arg1 = (T1)(object?)value!; break; case 2: - Arg2 = (T2)(object)value!; + Arg2 = (T2)(object?)value!; break; case 3: - Arg3 = (T3)(object)value!; + Arg3 = (T3)(object?)value!; break; case 4: - Arg4 = (T4)(object)value!; + Arg4 = (T4)(object?)value!; break; case 5: - Arg5 = (T5)(object)value!; + Arg5 = (T5)(object?)value!; break; default: break; @@ -748,7 +748,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { @@ -780,25 +780,25 @@ public object? this[int index] switch (index) { case 0: - Arg0 = (T0)(object)value!; + Arg0 = (T0)(object?)value!; break; case 1: - Arg1 = (T1)(object)value!; + Arg1 = (T1)(object?)value!; break; case 2: - Arg2 = (T2)(object)value!; + Arg2 = (T2)(object?)value!; break; case 3: - Arg3 = (T3)(object)value!; + Arg3 = (T3)(object?)value!; break; case 4: - Arg4 = (T4)(object)value!; + Arg4 = (T4)(object?)value!; break; case 5: - Arg5 = (T5)(object)value!; + Arg5 = (T5)(object?)value!; break; case 6: - Arg6 = (T6)(object)value!; + Arg6 = (T6)(object?)value!; break; default: break; @@ -896,7 +896,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) { @@ -930,28 +930,28 @@ public object? this[int index] switch (index) { case 0: - Arg0 = (T0)(object)value!; + Arg0 = (T0)(object?)value!; break; case 1: - Arg1 = (T1)(object)value!; + Arg1 = (T1)(object?)value!; break; case 2: - Arg2 = (T2)(object)value!; + Arg2 = (T2)(object?)value!; break; case 3: - Arg3 = (T3)(object)value!; + Arg3 = (T3)(object?)value!; break; case 4: - Arg4 = (T4)(object)value!; + Arg4 = (T4)(object?)value!; break; case 5: - Arg5 = (T5)(object)value!; + Arg5 = (T5)(object?)value!; break; case 6: - Arg6 = (T6)(object)value!; + Arg6 = (T6)(object?)value!; break; case 7: - Arg7 = (T7)(object)value!; + Arg7 = (T7)(object?)value!; break; default: break; @@ -1051,7 +1051,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) { @@ -1087,31 +1087,31 @@ public object? this[int index] switch (index) { case 0: - Arg0 = (T0)(object)value!; + Arg0 = (T0)(object?)value!; break; case 1: - Arg1 = (T1)(object)value!; + Arg1 = (T1)(object?)value!; break; case 2: - Arg2 = (T2)(object)value!; + Arg2 = (T2)(object?)value!; break; case 3: - Arg3 = (T3)(object)value!; + Arg3 = (T3)(object?)value!; break; case 4: - Arg4 = (T4)(object)value!; + Arg4 = (T4)(object?)value!; break; case 5: - Arg5 = (T5)(object)value!; + Arg5 = (T5)(object?)value!; break; case 6: - Arg6 = (T6)(object)value!; + Arg6 = (T6)(object?)value!; break; case 7: - Arg7 = (T7)(object)value!; + Arg7 = (T7)(object?)value!; break; case 8: - Arg8 = (T8)(object)value!; + Arg8 = (T8)(object?)value!; break; default: break; @@ -1213,7 +1213,7 @@ IEnumerator IEnumerable.GetEnumerator() } } -internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContextBase, IList +internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) { @@ -1251,34 +1251,34 @@ public object? this[int index] switch (index) { case 0: - Arg0 = (T0)(object)value!; + Arg0 = (T0)(object?)value!; break; case 1: - Arg1 = (T1)(object)value!; + Arg1 = (T1)(object?)value!; break; case 2: - Arg2 = (T2)(object)value!; + Arg2 = (T2)(object?)value!; break; case 3: - Arg3 = (T3)(object)value!; + Arg3 = (T3)(object?)value!; break; case 4: - Arg4 = (T4)(object)value!; + Arg4 = (T4)(object?)value!; break; case 5: - Arg5 = (T5)(object)value!; + Arg5 = (T5)(object?)value!; break; case 6: - Arg6 = (T6)(object)value!; + Arg6 = (T6)(object?)value!; break; case 7: - Arg7 = (T7)(object)value!; + Arg7 = (T7)(object?)value!; break; case 8: - Arg8 = (T8)(object)value!; + Arg8 = (T8)(object?)value!; break; case 9: - Arg9 = (T9)(object)value!; + Arg9 = (T9)(object?)value!; break; default: break; diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt index a9050134ee95..56afa55ba36d 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt @@ -37,7 +37,7 @@ internal sealed class RouteHandlerInvocationContext<<# foreach (var argumentCoun switch (index) { <# foreach (var argumentCount in Enumerable.Range(0, arity)) { #> case <#=argumentCount#>: - Arg<#=argumentCount#> = (T<#=argumentCount#>)(object)value!; + Arg<#=argumentCount#> = (T<#=argumentCount#>)(object?)value!; break; <# } #> default: diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index 3256efa6bc9f..d39a2525cf21 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -95,12 +95,12 @@ public static partial class RequestDelegateFactory 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 RouteHandlerInvocationContextBaseGetArgument = typeof(RouteHandlerInvocationContextBase).GetMethod(nameof(RouteHandlerInvocationContextBase.GetArgument))!; - private static readonly ParameterExpression FilterContextExpr = Expression.Parameter(typeof(RouteHandlerInvocationContextBase), "context"); - private static readonly MemberExpression FilterContextHttpContextExpr = Expression.Property(FilterContextExpr, typeof(RouteHandlerInvocationContextBase).GetProperty(nameof(RouteHandlerInvocationContextBase.HttpContext))!); + private static readonly MethodInfo RouteHandlerInvocationContextGetArgument = typeof(RouteHandlerInvocationContext).GetMethod(nameof(RouteHandlerInvocationContext.GetArgument))!; + 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 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(RouteHandlerInvocationContextBase), "filterContext"); + private static readonly ParameterExpression InvokedFilterContextExpr = Expression.Parameter(typeof(RouteHandlerInvocationContext), "filterContext"); private static readonly string[] DefaultAcceptsContentType = new[] { "application/json" }; private static readonly string[] FormFileContentType = new[] { "multipart/form-data" }; @@ -232,9 +232,9 @@ private static FactoryContext CreateFactoryContext(RequestDelegateFactoryOptions if (factoryContext.Filters is { Count: > 0 }) { var filterPipeline = CreateFilterPipeline(methodInfo, targetExpression, factoryContext, targetFactory); - Expression>> invokePipeline = (context) => filterPipeline(context); + Expression>> invokePipeline = (context) => filterPipeline(context); returnType = typeof(ValueTask); - // var filterContext = new RouteHandlerInvocationContextBase(httpContext, name_local, int_local); + // var filterContext = new RouteHandlerInvocationContext(httpContext, name_local, int_local); // invokePipeline.Invoke(filterContext); factoryContext.MethodCall = Expression.Block( new[] { InvokedFilterContextExpr }, @@ -273,10 +273,10 @@ private static RouteHandlerFilterDelegate CreateFilterPipeline(MethodInfo method // When `handler` returns an object, we generate the following wrapper // to convert it to `ValueTask` as expected in the filter // pipeline. - // ValueTask.FromResult(handler(RouteHandlerInvocationContextBase.GetArgument(0), RouteHandlerInvocationContextBase.GetArgument(1))); + // ValueTask.FromResult(handler(RouteHandlerInvocationContext.GetArgument(0), RouteHandlerInvocationContext.GetArgument(1))); // When the `handler` is a generic Task or ValueTask we await the task and // create a `ValueTask from the resulting value. - // new ValueTask(await handler(RouteHandlerInvocationContextBase.GetArgument(0), RouteHandlerInvocationContextBase.GetArgument(1))); + // new ValueTask(await handler(RouteHandlerInvocationContext.GetArgument(0), RouteHandlerInvocationContext.GetArgument(1))); // When the `handler` returns a void or a void-returning Task, then we return an EmptyHttpResult // to as a ValueTask // } @@ -307,7 +307,7 @@ targetExpression is null var currentFilterFactory = factoryContext.Filters[i]; var nextFilter = filteredInvocation; var currentFilter = currentFilterFactory(routeHandlerContext, nextFilter); - filteredInvocation = (RouteHandlerInvocationContextBase context) => currentFilter(context); + filteredInvocation = (RouteHandlerInvocationContext context) => currentFilter(context); } return filteredInvocation; @@ -383,7 +383,7 @@ private static Expression CreateRouteHandlerInvocationContextBase(FactoryContext { // In the event that the a constructor matching the arity of the // provided parameters is not found, we fall back to using the - // non-generic implementation of RouteHandlerInvocationContextBase. + // non-generic implementation of RouteHandlerInvocationContext. var fallbackConstruction = Expression.New( DefaultRouteHandlerInvocationContextConstructor, new Expression[] { HttpContextExpr, Expression.NewArrayInit(typeof(object), factoryContext.BoxedArgs) }); @@ -412,15 +412,15 @@ private static Expression CreateRouteHandlerInvocationContextBase(FactoryContext var constructor = constructorType.MakeGenericType(factoryContext.ArgumentTypes!).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).SingleOrDefault(); if (constructor == null) { - // new RouteHandlerInvocationContextBase(httpContext, (object)name_local, (object)int_local); + // new RouteHandlerInvocationContext(httpContext, (object)name_local, (object)int_local); return fallbackConstruction; } - // new RouteHandlerInvocationContextBase(httpContext, name_local, int_local); + // new RouteHandlerInvocationContext(httpContext, name_local, int_local); return Expression.New(constructor, arguments); } - // new RouteHandlerInvocationContextBase(httpContext, (object)name_local, (object)int_local); + // new RouteHandlerInvocationContext(httpContext, (object)name_local, (object)int_local); return fallbackConstruction; } @@ -508,10 +508,10 @@ private static Expression[] CreateArguments(ParameterInfo[]? parameters, Factory { args[i] = CreateArgument(parameters[i], factoryContext); // Register expressions containing the boxed and unboxed variants - // of the route handler's arguments for use in RouteHandlerInvocationContextBase + // of the route handler's arguments for use in RouteHandlerInvocationContext // construction and route handler invocation. // context.GetArgument(0) - factoryContext.ContextArgAccess.Add(Expression.Call(FilterContextExpr, RouteHandlerInvocationContextBaseGetArgument.MakeGenericMethod(parameters[i].ParameterType), Expression.Constant(i))); + factoryContext.ContextArgAccess.Add(Expression.Call(FilterContextExpr, RouteHandlerInvocationContextGetArgument.MakeGenericMethod(parameters[i].ParameterType), Expression.Constant(i))); // (string, name_local), (int, int_local) factoryContext.ArgumentTypes[i] = parameters[i].ParameterType; factoryContext.ArgumentExpressions[i] = args[i]; diff --git a/src/Http/Routing/src/Builder/RouteHandlerFilterExtensions.cs b/src/Http/Routing/src/Builder/RouteHandlerFilterExtensions.cs index ca5b618077e3..f0032cc2bf73 100644 --- a/src/Http/Routing/src/Builder/RouteHandlerFilterExtensions.cs +++ b/src/Http/Routing/src/Builder/RouteHandlerFilterExtensions.cs @@ -62,7 +62,7 @@ public static RouteHandlerBuilder AddFilter(this RouteHandlerBuilder builder, IR /// The . /// A representing the core logic of the filter. /// A that can be used to further customize the route handler. - public static RouteHandlerBuilder AddFilter(this RouteHandlerBuilder builder, Func> routeHandlerFilter) + public static RouteHandlerBuilder AddFilter(this RouteHandlerBuilder builder, Func> routeHandlerFilter) { builder.RouteHandlerFilterFactories.Add((routeHandlerContext, next) => (context) => routeHandlerFilter(context, next)); return builder; diff --git a/src/Http/Routing/src/PublicAPI.Unshipped.txt b/src/Http/Routing/src/PublicAPI.Unshipped.txt index c617932c4875..632d25fbd8af 100644 --- a/src/Http/Routing/src/PublicAPI.Unshipped.txt +++ b/src/Http/Routing/src/PublicAPI.Unshipped.txt @@ -13,7 +13,7 @@ override Microsoft.AspNetCore.Routing.RouteValuesAddress.ToString() -> string? Microsoft.AspNetCore.Routing.DefaultInlineConstraintResolver.DefaultInlineConstraintResolver(Microsoft.Extensions.Options.IOptions! routeOptions, System.IServiceProvider! serviceProvider) -> void static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddFilter(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, Microsoft.AspNetCore.Http.IRouteHandlerFilter! filter) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddFilter(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, System.Func! filterFactory) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! -static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddFilter(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, System.Func>! routeHandlerFilter) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddFilter(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, System.Func>! routeHandlerFilter) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddFilter(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.WithDescription(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, string! description) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.WithSummary(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, string! summary) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! diff --git a/src/Http/Routing/test/UnitTests/Builder/RouteHandlerEndpointRouteBuilderExtensionsTest.cs b/src/Http/Routing/test/UnitTests/Builder/RouteHandlerEndpointRouteBuilderExtensionsTest.cs index d5b9eb9dd420..4f7db98695cf 100644 --- a/src/Http/Routing/test/UnitTests/Builder/RouteHandlerEndpointRouteBuilderExtensionsTest.cs +++ b/src/Http/Routing/test/UnitTests/Builder/RouteHandlerEndpointRouteBuilderExtensionsTest.cs @@ -983,7 +983,7 @@ public ServiceAccessingRouteHandlerFilter(ILoggerFactory loggerFactory, RouteHan _routeHandlerContext = routeHandlerContext; } - public async ValueTask InvokeAsync(RouteHandlerInvocationContextBase context, RouteHandlerFilterDelegate next) + public async ValueTask InvokeAsync(RouteHandlerInvocationContext context, RouteHandlerFilterDelegate next) { context.HttpContext.Items["loggerErrorIsEnabled"] = _logger.IsEnabled(LogLevel.Error); context.HttpContext.Items["parentName"] = _routeHandlerContext.MethodInfo.DeclaringType?.Name; @@ -993,7 +993,7 @@ public ServiceAccessingRouteHandlerFilter(ILoggerFactory loggerFactory, RouteHan class IncrementArgFilter : IRouteHandlerFilter { - public async ValueTask InvokeAsync(RouteHandlerInvocationContextBase context, RouteHandlerFilterDelegate next) + public async ValueTask InvokeAsync(RouteHandlerInvocationContext context, RouteHandlerFilterDelegate next) { context.Arguments[0] = ((int)context.Arguments[0]!) + 1; return await next(context); From 5ce83fc152235b7a73098fcbcc6744965f1e4d0c Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Tue, 3 May 2022 09:57:59 -0700 Subject: [PATCH 5/6] Address more feedback from review --- ...rosoft.AspNetCore.Http.Abstractions.csproj | 8 ++--- ...eHandlerInvocationContextOfT.Generated.cs} | 11 +++++++ ...eHandlerInvocationContextOfT.Generated.tt} | 4 ++- ....AspNetCore.Http.Abstractions.Tests.csproj | 7 +++++ .../RouteHandlerInvocationContextOfTTests.cs | 29 +++++++++++++++++++ 5 files changed, 54 insertions(+), 5 deletions(-) rename src/Http/Http.Abstractions/src/{RouteHandlerInvocationContextOfT.cs => RouteHandlerInvocationContextOfT.Generated.cs} (98%) rename src/Http/Http.Abstractions/src/{RouteHandlerInvocationContextOfT.tt => RouteHandlerInvocationContextOfT.Generated.tt} (96%) diff --git a/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj b/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj index 93c45fd612bf..4c75d68b0ea1 100644 --- a/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj +++ b/src/Http/Http.Abstractions/src/Microsoft.AspNetCore.Http.Abstractions.csproj @@ -34,9 +34,9 @@ Microsoft.AspNetCore.Http.HttpResponse - + TextTemplatingFileGenerator - RouteHandlerInvocationContextOfT.cs + RouteHandlerInvocationContextOfT.Generated.cs @@ -45,10 +45,10 @@ Microsoft.AspNetCore.Http.HttpResponse - + True True - RouteHandlerInvocationContextOfT.tt + RouteHandlerInvocationContextOfT.Generated.tt diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.Generated.cs similarity index 98% rename from src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs rename to src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.Generated.cs index cbd1cfce3f29..8ed6a100d715 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.cs +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.Generated.cs @@ -4,9 +4,11 @@ #nullable enable using System.Collections; +using System.CodeDom.Compiler; namespace Microsoft.AspNetCore.Http; +[GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0) @@ -113,6 +115,7 @@ IEnumerator IEnumerable.GetEnumerator() } } +[GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1) @@ -226,6 +229,7 @@ IEnumerator IEnumerable.GetEnumerator() } } +[GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2) @@ -346,6 +350,7 @@ IEnumerator IEnumerable.GetEnumerator() } } +[GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3) @@ -473,6 +478,7 @@ IEnumerator IEnumerable.GetEnumerator() } } +[GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) @@ -607,6 +613,7 @@ IEnumerator IEnumerable.GetEnumerator() } } +[GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) @@ -748,6 +755,7 @@ IEnumerator IEnumerable.GetEnumerator() } } +[GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) @@ -896,6 +904,7 @@ IEnumerator IEnumerable.GetEnumerator() } } +[GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) @@ -1051,6 +1060,7 @@ IEnumerator IEnumerable.GetEnumerator() } } +[GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) @@ -1213,6 +1223,7 @@ IEnumerator IEnumerable.GetEnumerator() } } +[GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.Generated.tt similarity index 96% rename from src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt rename to src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.Generated.tt index 56afa55ba36d..7b638ccf9699 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.tt +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.Generated.tt @@ -11,11 +11,13 @@ int[] arities = Enumerable.Range(1, 10).ToArray(); #nullable enable using System.Collections; +using System.CodeDom.Compiler; namespace Microsoft.AspNetCore.Http; <# foreach (var arity in arities) { #> -internal sealed class RouteHandlerInvocationContext<<# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>T<#=argumentCount#><# if (argumentCount < arity - 1) { #>, <# } #><# } #>> : RouteHandlerInvocationContextBase, IList +[GeneratedCode("TextTemplatingFileGenerator", "")] +internal sealed class RouteHandlerInvocationContext<<# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>T<#=argumentCount#><# if (argumentCount < arity - 1) { #>, <# } #><# } #>> : RouteHandlerInvocationContext, IList { internal RouteHandlerInvocationContext(HttpContext httpContext, <# foreach (var argumentCount in Enumerable.Range(0, arity)) { #>T<#=argumentCount#> arg<#=argumentCount#><# if (argumentCount < arity - 1) { #>, <# } #><# } #>) { diff --git a/src/Http/Http.Abstractions/test/Microsoft.AspNetCore.Http.Abstractions.Tests.csproj b/src/Http/Http.Abstractions/test/Microsoft.AspNetCore.Http.Abstractions.Tests.csproj index b4bbecb268fe..f35cfb3b4398 100644 --- a/src/Http/Http.Abstractions/test/Microsoft.AspNetCore.Http.Abstractions.Tests.csproj +++ b/src/Http/Http.Abstractions/test/Microsoft.AspNetCore.Http.Abstractions.Tests.csproj @@ -12,6 +12,13 @@ + + + + + + + diff --git a/src/Http/Http.Abstractions/test/RouteHandlerInvocationContextOfTTests.cs b/src/Http/Http.Abstractions/test/RouteHandlerInvocationContextOfTTests.cs index 52f930c34312..f42f8d53c580 100644 --- a/src/Http/Http.Abstractions/test/RouteHandlerInvocationContextOfTTests.cs +++ b/src/Http/Http.Abstractions/test/RouteHandlerInvocationContextOfTTests.cs @@ -1,5 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Mono.TextTemplating; namespace Microsoft.AspNetCore.Http.Abstractions.Tests; @@ -55,6 +56,34 @@ public void AllowsEnumerationOfParameters() Assert.Equal(4, enumeratedCount); } + // Test for https://github.com/dotnet/aspnetcore/issues/41489 + [Fact] + public void HandlesMismatchedNullabilityOnTypeParams() + { + var context = new RouteHandlerInvocationContext(new DefaultHttpContext(), null, null, null, null); + // Mismatched reference types will resolve as null + Assert.Null(context.GetArgument(0)); + Assert.Null(context.GetArgument(3)); + // Mismatched value types will throw + Assert.Throws(() => context.GetArgument(1)); + Assert.Throws(() => context.GetArgument(2)); + } + + [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 generator = new TemplateGenerator(); + var compiledTemplate = generator.CompileTemplate(File.ReadAllText(templatePath)); + + var generatedContent = compiledTemplate.Process(); + var currentContent = File.ReadAllText(currentContentPath); + + Assert.Equal(currentContent, generatedContent); + } + interface ITodo { } class Todo : ITodo { } } From d87d6a7edeb1018417478ee6cff377647a356146 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Wed, 4 May 2022 10:20:10 -0700 Subject: [PATCH 6/6] Address more feedack --- .../src/DefaultRouteHandlerInvocationContext.cs | 2 +- .../src/RouteHandlerInvocationContext.cs | 2 +- .../src/RouteHandlerInvocationContextOfT.Generated.cs | 10 ---------- .../src/RouteHandlerInvocationContextOfT.Generated.tt | 1 - src/Http/Http.Extensions/src/RequestDelegateFactory.cs | 2 +- 5 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/Http/Http.Abstractions/src/DefaultRouteHandlerInvocationContext.cs b/src/Http/Http.Abstractions/src/DefaultRouteHandlerInvocationContext.cs index daa2096321c5..e20f24678041 100644 --- a/src/Http/Http.Abstractions/src/DefaultRouteHandlerInvocationContext.cs +++ b/src/Http/Http.Abstractions/src/DefaultRouteHandlerInvocationContext.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Http; /// Provides a default implementation for wrapping the and parameters /// provided to a route handler. /// -public class DefaultRouteHandlerInvocationContext : RouteHandlerInvocationContext +public sealed class DefaultRouteHandlerInvocationContext : RouteHandlerInvocationContext { /// /// Creates a new instance of the for a given request. diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs index 4ae318236593..2ed6755fdab6 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContext.cs @@ -27,6 +27,6 @@ public abstract class RouteHandlerInvocationContext /// /// The of the resolved argument. /// An integer representing the position of the argument in the argument list. - /// The argument at a given + /// The argument at a given . public abstract T GetArgument(int index); } diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.Generated.cs b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.Generated.cs index 8ed6a100d715..da7dd32ea3e4 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.Generated.cs +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.Generated.cs @@ -113,7 +113,6 @@ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - } [GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList @@ -227,7 +226,6 @@ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - } [GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList @@ -348,7 +346,6 @@ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - } [GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList @@ -476,7 +473,6 @@ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - } [GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList @@ -611,7 +607,6 @@ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - } [GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList @@ -753,7 +748,6 @@ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - } [GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList @@ -902,7 +896,6 @@ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - } [GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList @@ -1058,7 +1051,6 @@ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - } [GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList @@ -1221,7 +1213,6 @@ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - } [GeneratedCode("TextTemplatingFileGenerator", "")] internal sealed class RouteHandlerInvocationContext : RouteHandlerInvocationContext, IList @@ -1391,5 +1382,4 @@ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - } diff --git a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.Generated.tt b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.Generated.tt index 7b638ccf9699..6fcbe7075cde 100644 --- a/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.Generated.tt +++ b/src/Http/Http.Abstractions/src/RouteHandlerInvocationContextOfT.Generated.tt @@ -126,6 +126,5 @@ internal sealed class RouteHandlerInvocationContext<<# foreach (var argumentCoun { return GetEnumerator(); } - } <# } #> diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index d39a2525cf21..94d99f3a09e7 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -381,7 +381,7 @@ private static Expression MapHandlerReturnTypeToValueTask(Expression methodCall, private static Expression CreateRouteHandlerInvocationContextBase(FactoryContext factoryContext) { - // In the event that the a constructor matching the arity of the + // 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. var fallbackConstruction = Expression.New(