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