diff --git a/src/Http/Http.Extensions/src/Microsoft.AspNetCore.Http.Extensions.csproj b/src/Http/Http.Extensions/src/Microsoft.AspNetCore.Http.Extensions.csproj index 123ae96be7a6..94a3e73732a3 100644 --- a/src/Http/Http.Extensions/src/Microsoft.AspNetCore.Http.Extensions.csproj +++ b/src/Http/Http.Extensions/src/Microsoft.AspNetCore.Http.Extensions.csproj @@ -11,6 +11,7 @@ + diff --git a/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt index c82589d1209b..338549737153 100644 --- a/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt @@ -152,6 +152,7 @@ Microsoft.AspNetCore.Http.Headers.ResponseHeaders.Set(string! name, object? valu Microsoft.AspNetCore.Http.Headers.ResponseHeaders.SetCookie.get -> System.Collections.Generic.IList! Microsoft.AspNetCore.Http.Headers.ResponseHeaders.SetCookie.set -> void Microsoft.AspNetCore.Http.Headers.ResponseHeaders.SetList(string! name, System.Collections.Generic.IList? values) -> void +Microsoft.AspNetCore.Http.RequestDelegateFactory override Microsoft.AspNetCore.Http.Extensions.QueryBuilder.Equals(object? obj) -> bool override Microsoft.AspNetCore.Http.Extensions.QueryBuilder.ToString() -> string! static Microsoft.AspNetCore.Http.Extensions.HttpRequestMultipartExtensions.GetMultipartBoundary(this Microsoft.AspNetCore.Http.HttpRequest! request) -> string! @@ -168,6 +169,9 @@ static Microsoft.AspNetCore.Http.HeaderDictionaryTypeExtensions.AppendList(th static Microsoft.AspNetCore.Http.HeaderDictionaryTypeExtensions.GetTypedHeaders(this Microsoft.AspNetCore.Http.HttpRequest! request) -> Microsoft.AspNetCore.Http.Headers.RequestHeaders! static Microsoft.AspNetCore.Http.HeaderDictionaryTypeExtensions.GetTypedHeaders(this Microsoft.AspNetCore.Http.HttpResponse! response) -> Microsoft.AspNetCore.Http.Headers.ResponseHeaders! static Microsoft.AspNetCore.Http.HttpContextServerVariableExtensions.GetServerVariable(this Microsoft.AspNetCore.Http.HttpContext! context, string! variableName) -> string? +static Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(System.Delegate! action) -> Microsoft.AspNetCore.Http.RequestDelegate! +static Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(System.Reflection.MethodInfo! methodInfo) -> Microsoft.AspNetCore.Http.RequestDelegate! +static Microsoft.AspNetCore.Http.RequestDelegateFactory.Create(System.Reflection.MethodInfo! methodInfo, System.Func! targetFactory) -> Microsoft.AspNetCore.Http.RequestDelegate! static Microsoft.AspNetCore.Http.ResponseExtensions.Clear(this Microsoft.AspNetCore.Http.HttpResponse! response) -> void static Microsoft.AspNetCore.Http.ResponseExtensions.Redirect(this Microsoft.AspNetCore.Http.HttpResponse! response, string! location, bool permanent, bool preserveMethod) -> void static Microsoft.AspNetCore.Http.SendFileResponseExtensions.SendFileAsync(this Microsoft.AspNetCore.Http.HttpResponse! response, Microsoft.Extensions.FileProviders.IFileInfo! file, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task! diff --git a/src/Http/Routing/src/Internal/MapActionExpressionTreeBuilder.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs similarity index 79% rename from src/Http/Routing/src/Internal/MapActionExpressionTreeBuilder.cs rename to src/Http/Http.Extensions/src/RequestDelegateFactory.cs index 1e83d92c3497..948c77fd094c 100644 --- a/src/Http/Routing/src/Internal/MapActionExpressionTreeBuilder.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -11,24 +11,26 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Metadata; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Internal; using Microsoft.Extensions.Logging; -namespace Microsoft.AspNetCore.Routing.Internal +namespace Microsoft.AspNetCore.Http { - internal static class MapActionExpressionTreeBuilder + /// + /// Creates implementations from request handlers. + /// + public static class RequestDelegateFactory { private static readonly MethodInfo ChangeTypeMethodInfo = GetMethodInfo>((value, type) => Convert.ChangeType(value, type, CultureInfo.InvariantCulture)); - private static readonly MethodInfo ExecuteTaskOfTMethodInfo = typeof(MapActionExpressionTreeBuilder).GetMethod(nameof(ExecuteTask), BindingFlags.NonPublic | BindingFlags.Static)!; - private static readonly MethodInfo ExecuteTaskOfStringMethodInfo = typeof(MapActionExpressionTreeBuilder).GetMethod(nameof(ExecuteTaskOfString), BindingFlags.NonPublic | BindingFlags.Static)!; - private static readonly MethodInfo ExecuteValueTaskOfTMethodInfo = typeof(MapActionExpressionTreeBuilder).GetMethod(nameof(ExecuteValueTaskOfT), BindingFlags.NonPublic | BindingFlags.Static)!; - private static readonly MethodInfo ExecuteValueTaskMethodInfo = typeof(MapActionExpressionTreeBuilder).GetMethod(nameof(ExecuteValueTask), BindingFlags.NonPublic | BindingFlags.Static)!; - private static readonly MethodInfo ExecuteValueTaskOfStringMethodInfo = typeof(MapActionExpressionTreeBuilder).GetMethod(nameof(ExecuteValueTaskOfString), BindingFlags.NonPublic | BindingFlags.Static)!; - private static readonly MethodInfo ExecuteTaskResultOfTMethodInfo = typeof(MapActionExpressionTreeBuilder).GetMethod(nameof(ExecuteTaskResult), BindingFlags.NonPublic | BindingFlags.Static)!; - private static readonly MethodInfo ExecuteValueResultTaskOfTMethodInfo = typeof(MapActionExpressionTreeBuilder).GetMethod(nameof(ExecuteValueTaskResult), BindingFlags.NonPublic | BindingFlags.Static)!; + private static readonly MethodInfo ExecuteTaskOfTMethodInfo = typeof(RequestDelegateFactory).GetMethod(nameof(ExecuteTask), BindingFlags.NonPublic | BindingFlags.Static)!; + private static readonly MethodInfo ExecuteTaskOfStringMethodInfo = typeof(RequestDelegateFactory).GetMethod(nameof(ExecuteTaskOfString), BindingFlags.NonPublic | BindingFlags.Static)!; + private static readonly MethodInfo ExecuteValueTaskOfTMethodInfo = typeof(RequestDelegateFactory).GetMethod(nameof(ExecuteValueTaskOfT), BindingFlags.NonPublic | BindingFlags.Static)!; + private static readonly MethodInfo ExecuteValueTaskMethodInfo = typeof(RequestDelegateFactory).GetMethod(nameof(ExecuteValueTask), BindingFlags.NonPublic | BindingFlags.Static)!; + private static readonly MethodInfo ExecuteValueTaskOfStringMethodInfo = typeof(RequestDelegateFactory).GetMethod(nameof(ExecuteValueTaskOfString), BindingFlags.NonPublic | BindingFlags.Static)!; + private static readonly MethodInfo ExecuteTaskResultOfTMethodInfo = typeof(RequestDelegateFactory).GetMethod(nameof(ExecuteTaskResult), BindingFlags.NonPublic | BindingFlags.Static)!; + private static readonly MethodInfo ExecuteValueResultTaskOfTMethodInfo = typeof(RequestDelegateFactory).GetMethod(nameof(ExecuteValueTaskResult), BindingFlags.NonPublic | BindingFlags.Static)!; private static readonly MethodInfo GetRequiredServiceMethodInfo = typeof(ServiceProviderServiceExtensions).GetMethod(nameof(ServiceProviderServiceExtensions.GetRequiredService), BindingFlags.Public | BindingFlags.Static, new Type[] { typeof(IServiceProvider) })!; private static readonly MethodInfo ResultWriteResponseAsync = typeof(IResult).GetMethod(nameof(IResult.ExecuteAsync), BindingFlags.Public | BindingFlags.Instance)!; private static readonly MethodInfo StringResultWriteResponseAsync = GetMethodInfo>((response, text) => HttpResponseWritingExtensions.WriteAsync(response, text, default)); @@ -44,7 +46,85 @@ internal static class MapActionExpressionTreeBuilder private static readonly MemberExpression HttpResponseExpr = Expression.Property(HttpContextParameter, nameof(HttpContext.Response)); private static readonly MemberExpression RequestAbortedExpr = Expression.Property(HttpContextParameter, nameof(HttpContext.RequestAborted)); - public static RequestDelegate BuildRequestDelegate(Delegate action) + /// + /// Creates a implementation for . + /// + /// A request handler with any number of custom parameters that often produces a response with its return value. + /// The . + public static RequestDelegate Create(Delegate action) + { + if (action is null) + { + throw new ArgumentNullException(nameof(action)); + } + + var targetExpression = action.Target switch + { + object => Expression.Convert(TargetArg, action.Target.GetType()), + null => null, + }; + + var untargetedRequestDelegate = CreateRequestDelegate(action.Method, targetExpression); + + return httpContext => + { + return untargetedRequestDelegate(action.Target, httpContext); + }; + } + + /// + /// Creates a implementation for . + /// + /// A static request handler with any number of custom parameters that often produces a response with its return value. + /// The . + public static RequestDelegate Create(MethodInfo methodInfo) + { + if (methodInfo is null) + { + throw new ArgumentNullException(nameof(methodInfo)); + } + + var untargetedRequestDelegate = CreateRequestDelegate(methodInfo, targetExpression: null); + + return httpContext => + { + return untargetedRequestDelegate(null, httpContext); + }; + } + + /// + /// Creates a implementation for . + /// + /// A request handler with any number of custom parameters that often produces a response with its return value. + /// Creates the for the non-static method. + /// The . + public static RequestDelegate Create(MethodInfo methodInfo, Func targetFactory) + { + if (methodInfo is null) + { + throw new ArgumentNullException(nameof(methodInfo)); + } + + if (targetFactory is null) + { + throw new ArgumentNullException(nameof(targetFactory)); + } + + if (methodInfo.DeclaringType is null) + { + throw new ArgumentException($"A {nameof(targetFactory)} was provided, but {nameof(methodInfo)} does not have a Declaring type."); + } + + var targetExpression = Expression.Convert(TargetArg, methodInfo.DeclaringType); + var untargetedRequestDelegate = CreateRequestDelegate(methodInfo, targetExpression); + + return httpContext => + { + return untargetedRequestDelegate(targetFactory(httpContext), httpContext); + }; + } + + private static Func CreateRequestDelegate(MethodInfo methodInfo, Expression? targetExpression) { // Non void return type @@ -62,8 +142,6 @@ public static RequestDelegate BuildRequestDelegate(Delegate action) // return default; // } - var method = action.Method; - var consumeBodyDirectly = false; var consumeBodyAsForm = false; Type? bodyType = null; @@ -72,7 +150,7 @@ public static RequestDelegate BuildRequestDelegate(Delegate action) // This argument represents the deserialized body returned from IHttpRequestReader // when the method has a FromBody attribute declared - var methodParameters = method.GetParameters(); + var methodParameters = methodInfo.GetParameters(); var args = new List(methodParameters.Length); foreach (var parameter in methodParameters) @@ -156,18 +234,17 @@ public static RequestDelegate BuildRequestDelegate(Delegate action) MethodCallExpression methodCall; - if (action.Target is null) + if (targetExpression is null) { - methodCall = Expression.Call(method, args); + methodCall = Expression.Call(methodInfo, args); } else { - var castedTarget = Expression.Convert(TargetArg, action.Target.GetType()); - methodCall = Expression.Call(castedTarget, method, args); + methodCall = Expression.Call(targetExpression, methodInfo, args); } // Exact request delegate match - if (method.ReturnType == typeof(void)) + if (methodInfo.ReturnType == typeof(void)) { var bodyExpressions = new List { @@ -177,22 +254,22 @@ public static RequestDelegate BuildRequestDelegate(Delegate action) body = Expression.Block(bodyExpressions); } - else if (AwaitableInfo.IsTypeAwaitable(method.ReturnType, out var info)) + else if (AwaitableInfo.IsTypeAwaitable(methodInfo.ReturnType, out var info)) { - if (method.ReturnType == typeof(Task)) + if (methodInfo.ReturnType == typeof(Task)) { body = methodCall; } - else if (method.ReturnType == typeof(ValueTask)) + else if (methodInfo.ReturnType == typeof(ValueTask)) { body = Expression.Call( ExecuteValueTaskMethodInfo, methodCall); } - else if (method.ReturnType.IsGenericType && - method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) + else if (methodInfo.ReturnType.IsGenericType && + methodInfo.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) { - var typeArg = method.ReturnType.GetGenericArguments()[0]; + var typeArg = methodInfo.ReturnType.GetGenericArguments()[0]; if (typeof(IResult).IsAssignableFrom(typeArg)) { @@ -220,10 +297,10 @@ public static RequestDelegate BuildRequestDelegate(Delegate action) } } } - else if (method.ReturnType.IsGenericType && - method.ReturnType.GetGenericTypeDefinition() == typeof(ValueTask<>)) + else if (methodInfo.ReturnType.IsGenericType && + methodInfo.ReturnType.GetGenericTypeDefinition() == typeof(ValueTask<>)) { - var typeArg = method.ReturnType.GetGenericArguments()[0]; + var typeArg = methodInfo.ReturnType.GetGenericArguments()[0]; if (typeof(IResult).IsAssignableFrom(typeArg)) { @@ -254,18 +331,18 @@ public static RequestDelegate BuildRequestDelegate(Delegate action) else { // TODO: Handle custom awaitables - throw new NotSupportedException($"Unsupported return type: {method.ReturnType}"); + throw new NotSupportedException($"Unsupported return type: {methodInfo.ReturnType}"); } } - else if (typeof(IResult).IsAssignableFrom(method.ReturnType)) + else if (typeof(IResult).IsAssignableFrom(methodInfo.ReturnType)) { body = Expression.Call(methodCall, ResultWriteResponseAsync, HttpContextParameter); } - else if (method.ReturnType == typeof(string)) + else if (methodInfo.ReturnType == typeof(string)) { body = Expression.Call(StringResultWriteResponseAsync, HttpResponseExpr, methodCall, Expression.Constant(CancellationToken.None)); } - else if (method.ReturnType.IsValueType) + else if (methodInfo.ReturnType.IsValueType) { var box = Expression.TypeAs(methodCall, typeof(object)); body = Expression.Call(JsonResultWriteResponseAsync, HttpResponseExpr, box, Expression.Constant(CancellationToken.None)); @@ -357,10 +434,7 @@ public static RequestDelegate BuildRequestDelegate(Delegate action) requestDelegate = invoker; } - return httpContext => - { - return requestDelegate(action.Target, httpContext); - }; + return requestDelegate; } private static ILogger GetLogger(HttpContext httpContext) diff --git a/src/Http/Routing/test/UnitTests/Internal/MapActionExpressionTreeBuilderTest.cs b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs similarity index 85% rename from src/Http/Routing/test/UnitTests/Internal/MapActionExpressionTreeBuilderTest.cs rename to src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs index 39372600077f..36d4004cfbe3 100644 --- a/src/Http/Routing/test/UnitTests/Internal/MapActionExpressionTreeBuilderTest.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Reflection; using System.Text; using System.Text.Json; using System.Threading; @@ -22,7 +23,7 @@ namespace Microsoft.AspNetCore.Routing.Internal { - public class MapActionExpressionTreeBuilderTest + public class RequestDelegateFactoryTests { public static IEnumerable NoResult { @@ -85,13 +86,105 @@ public async Task RequestDelegateInvokesAction(Delegate @delegate) { var httpContext = new DefaultHttpContext(); - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate(@delegate); + var requestDelegate = RequestDelegateFactory.Create(@delegate); await requestDelegate(httpContext); Assert.True(httpContext.Items["invoked"] as bool?); } + private static void StaticTestActionBasicReflection(HttpContext httpContext) + { + httpContext.Items.Add("invoked", true); + } + + [Fact] + public async Task StaticMethodInfoOverloadWorksWithBasicReflection() + { + var methodInfo = typeof(RequestDelegateFactoryTests).GetMethod( + nameof(StaticTestActionBasicReflection), + BindingFlags.NonPublic | BindingFlags.Static, + new[] { typeof(HttpContext) }); + + var requestDelegate = RequestDelegateFactory.Create(methodInfo!); + + var httpContext = new DefaultHttpContext(); + + await requestDelegate(httpContext); + + Assert.True(httpContext.Items["invoked"] as bool?); + } + + private class TestNonStaticActionClass + { + private readonly object _invokedValue; + + public TestNonStaticActionClass(object invokedValue) + { + _invokedValue = invokedValue; + } + + private void NonStaticTestAction(HttpContext httpContext) + { + httpContext.Items.Add("invoked", _invokedValue); + } + } + + [Fact] + public async Task NonStaticMethodInfoOverloadWorksWithBasicReflection() + { + var methodInfo = typeof(TestNonStaticActionClass).GetMethod( + "NonStaticTestAction", + BindingFlags.NonPublic | BindingFlags.Instance, + new[] { typeof(HttpContext) }); + + var invoked = false; + + object GetTarget() + { + if (!invoked) + { + invoked = true; + return new TestNonStaticActionClass(1); + } + + return new TestNonStaticActionClass(2); + } + + var requestDelegate = RequestDelegateFactory.Create(methodInfo!, _ => GetTarget()); + + var httpContext = new DefaultHttpContext(); + + await requestDelegate(httpContext); + + Assert.Equal(1, httpContext.Items["invoked"]); + + httpContext = new DefaultHttpContext(); + + await requestDelegate(httpContext); + + Assert.Equal(2, httpContext.Items["invoked"]); + } + + [Fact] + public void BuildRequestDelegateThrowsArgumentNullExceptions() + { + var methodInfo = typeof(RequestDelegateFactoryTests).GetMethod( + nameof(StaticTestActionBasicReflection), + BindingFlags.NonPublic | BindingFlags.Static, + new[] { typeof(HttpContext) }); + + var exNullAction = Assert.Throws(() => RequestDelegateFactory.Create(action: null!)); + var exNullMethodInfo1 = Assert.Throws(() => RequestDelegateFactory.Create(methodInfo: null!)); + var exNullMethodInfo2 = Assert.Throws(() => RequestDelegateFactory.Create(methodInfo: null!, _ => 0)); + var exNullTargetFactory = Assert.Throws(() => RequestDelegateFactory.Create(methodInfo!, targetFactory: null!)); + + Assert.Equal("action", exNullAction.ParamName); + Assert.Equal("methodInfo", exNullMethodInfo1.ParamName); + Assert.Equal("methodInfo", exNullMethodInfo2.ParamName); + Assert.Equal("targetFactory", exNullTargetFactory.ParamName); + } + public static IEnumerable FromRouteResult { get @@ -136,7 +229,7 @@ public async Task RequestDelegatePopulatesFromRouteParameterBasedOnParameterName var httpContext = new DefaultHttpContext(); httpContext.Request.RouteValues[paramName] = originalRouteParam.ToString(NumberFormatInfo.InvariantInfo); - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate(@delegate); + var requestDelegate = RequestDelegateFactory.Create(@delegate); await requestDelegate(httpContext); @@ -179,7 +272,7 @@ public async Task RequestDelegatePopulatesFromRouteOptionalParameter(Delegate @d { var httpContext = new DefaultHttpContext(); - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate(@delegate); + var requestDelegate = RequestDelegateFactory.Create(@delegate); await requestDelegate(httpContext); @@ -197,7 +290,7 @@ public async Task RequestDelegatePopulatesFromRouteOptionalParameterBasedOnParam httpContext.Request.RouteValues[paramName] = originalRouteParam.ToString(NumberFormatInfo.InvariantInfo); - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate(@delegate); + var requestDelegate = RequestDelegateFactory.Create(@delegate); await requestDelegate(httpContext); @@ -220,7 +313,7 @@ void TestAction([FromRoute(Name = specifiedName)] int foo) var httpContext = new DefaultHttpContext(); httpContext.Request.RouteValues[specifiedName] = originalRouteParam.ToString(NumberFormatInfo.InvariantInfo); - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -243,7 +336,7 @@ void TestAction([FromRoute] int foo) var httpContext = new DefaultHttpContext(); httpContext.Request.RouteValues[unmatchedName] = unmatchedRouteParam.ToString(NumberFormatInfo.InvariantInfo); - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -271,7 +364,7 @@ void TestAction([FromQuery] int value) var httpContext = new DefaultHttpContext(); httpContext.Request.Query = query; - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -294,7 +387,7 @@ void TestAction([FromHeader(Name = customHeaderName)] int value) var httpContext = new DefaultHttpContext(); httpContext.Request.Headers[customHeaderName] = originalHeaderParam.ToString(NumberFormatInfo.InvariantInfo); - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -322,7 +415,7 @@ void TestAction([FromBody] Todo todo) var requestBodyBytes = JsonSerializer.SerializeToUtf8Bytes(originalTodo); httpContext.Request.Body = new MemoryStream(requestBodyBytes); - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -341,7 +434,7 @@ void TestAction([FromBody] Todo todo) httpContext.Request.Headers["Content-Type"] = "application/json"; httpContext.Request.Headers["Content-Length"] = "0"; - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await Assert.ThrowsAsync(() => requestDelegate(httpContext)); } @@ -360,7 +453,7 @@ void TestAction([FromBody(AllowEmpty = true)] Todo todo) httpContext.Request.Headers["Content-Type"] = "application/json"; httpContext.Request.Headers["Content-Length"] = "0"; - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -384,7 +477,7 @@ void TestAction([FromBody(AllowEmpty = true)] BodyStruct bodyStruct) httpContext.Request.Headers["Content-Type"] = "application/json"; httpContext.Request.Headers["Content-Length"] = "0"; - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -414,7 +507,7 @@ void TestAction([FromBody] Todo todo) httpContext.Features.Set(new TestHttpRequestLifetimeFeature()); httpContext.RequestServices = serviceCollection.BuildServiceProvider(); - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -450,7 +543,7 @@ void TestAction([FromBody] Todo todo) httpContext.Features.Set(new TestHttpRequestLifetimeFeature()); httpContext.RequestServices = serviceCollection.BuildServiceProvider(); - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -485,7 +578,7 @@ void TestAction([FromForm] int value) var httpContext = new DefaultHttpContext(); httpContext.Request.Form = form; - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -515,7 +608,7 @@ void TestAction([FromForm] int value) httpContext.Features.Set(new TestHttpRequestLifetimeFeature()); httpContext.RequestServices = serviceCollection.BuildServiceProvider(); - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -551,7 +644,7 @@ void TestAction([FromForm] int value) httpContext.Features.Set(new TestHttpRequestLifetimeFeature()); httpContext.RequestServices = serviceCollection.BuildServiceProvider(); - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -571,8 +664,8 @@ public void BuildRequestDelegateThrowsInvalidOperationExceptionGivenBothFromBody void TestAction([FromBody] int value1, [FromForm] int value2) { } void TestActionWithFlippedParams([FromForm] int value1, [FromBody] int value2) { } - Assert.Throws(() => MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction)); - Assert.Throws(() => MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestActionWithFlippedParams)); + Assert.Throws(() => RequestDelegateFactory.Create((Action)TestAction)); + Assert.Throws(() => RequestDelegateFactory.Create((Action)TestActionWithFlippedParams)); } [Fact] @@ -580,7 +673,7 @@ public void BuildRequestDelegateThrowsInvalidOperationExceptionGivenFromBodyOnMu { void TestAction([FromBody] int value1, [FromBody] int value2) { } - Assert.Throws(() => MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction)); + Assert.Throws(() => RequestDelegateFactory.Create((Action)TestAction)); } [Fact] @@ -600,7 +693,7 @@ void TestAction([FromService] MyService myService) var httpContext = new DefaultHttpContext(); httpContext.RequestServices = serviceCollection.BuildServiceProvider(); - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -619,7 +712,7 @@ void TestAction(HttpContext httpContext) var httpContext = new DefaultHttpContext(); - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -639,7 +732,7 @@ void TestAction(IFormCollection httpContext) var httpContext = new DefaultHttpContext(); httpContext.Request.Headers["Content-Type"] = "application/x-www-form-urlencoded"; - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -662,7 +755,7 @@ void TestAction(CancellationToken cancellationToken) RequestAborted = cts.Token }; - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate((Action)TestAction); + var requestDelegate = RequestDelegateFactory.Create((Action)TestAction); await requestDelegate(httpContext); @@ -706,7 +799,7 @@ public async Task RequestDelegateWritesComplexReturnValueAsJsonResponseBody(Dele var responseBodyStream = new MemoryStream(); httpContext.Response.Body = responseBodyStream; - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate(@delegate); + var requestDelegate = RequestDelegateFactory.Create(@delegate); await requestDelegate(httpContext); @@ -755,7 +848,7 @@ public async Task RequestDelegateUsesCustomIResult(Delegate @delegate) var responseBodyStream = new MemoryStream(); httpContext.Response.Body = responseBodyStream; - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate(@delegate); + var requestDelegate = RequestDelegateFactory.Create(@delegate); await requestDelegate(httpContext); @@ -798,7 +891,7 @@ public async Task RequestDelegateWritesStringReturnValueAsJsonResponseBody(Deleg var responseBodyStream = new MemoryStream(); httpContext.Response.Body = responseBodyStream; - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate(@delegate); + var requestDelegate = RequestDelegateFactory.Create(@delegate); await requestDelegate(httpContext); @@ -839,7 +932,7 @@ public async Task RequestDelegateWritesIntReturnValue(Delegate @delegate) var responseBodyStream = new MemoryStream(); httpContext.Response.Body = responseBodyStream; - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate(@delegate); + var requestDelegate = RequestDelegateFactory.Create(@delegate); await requestDelegate(httpContext); @@ -880,7 +973,7 @@ public async Task RequestDelegateWritesBoolReturnValue(Delegate @delegate) var responseBodyStream = new MemoryStream(); httpContext.Response.Body = responseBodyStream; - var requestDelegate = MapActionExpressionTreeBuilder.BuildRequestDelegate(@delegate); + var requestDelegate = RequestDelegateFactory.Create(@delegate); await requestDelegate(httpContext); diff --git a/src/Http/Routing/src/Builder/MapActionEndpointRouteBuilderExtensions.cs b/src/Http/Routing/src/Builder/MapActionEndpointRouteBuilderExtensions.cs index e816c1cdb779..e071bae5ac81 100644 --- a/src/Http/Routing/src/Builder/MapActionEndpointRouteBuilderExtensions.cs +++ b/src/Http/Routing/src/Builder/MapActionEndpointRouteBuilderExtensions.cs @@ -5,8 +5,8 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Patterns; namespace Microsoft.AspNetCore.Builder @@ -159,7 +159,7 @@ public static MapActionEndpointConventionBuilder Map( const int defaultOrder = 0; var builder = new RouteEndpointBuilder( - MapActionExpressionTreeBuilder.BuildRequestDelegate(action), + RequestDelegateFactory.Create(action), pattern, defaultOrder) { diff --git a/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj b/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj index 67723bd1d175..e96f32ceddf5 100644 --- a/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj +++ b/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj @@ -24,7 +24,6 @@ Microsoft.AspNetCore.Routing.RouteCollection -