Skip to content

Add API to turn delegate or MethodInfo into a RequestDelegate  #31181

Closed
@davidfowl

Description

@davidfowl

Background and Motivation

MVC Controllers have this magic power baked inside of them to allow dispatching HTTP requests to an arbitrarily shaped method on a controller (called actions). As part of the work to make this more accessible, we want to expose a zero dependency building block from lower in the stack that enables the same thing with arbitrary methods and delegates. This lets library authors build convenient and complex request handling logic without having to take big dependencies on controllers or mvc.

Proposed API

namespace Microsoft.AspNetCore.Http
{
+    public sealed class RequestDelegateFactory
+    {
+        public static RequestDelegate Create(Delegate requestHandler);
+        public static RequestDelegate Create(MethodInfo methodInfo, Func<HttpContext, object> targetFactory);
+        public static RequestDelegate Create(MethodInfo methodInfo);
+    }
}

Usage Examples

There are 2 scenarios

  1. Wrapping a fixed Delegate
  2. MethodInfo with or without "this"

Enhanced routing (MapAction) with is number 1, MVC controllers are examples of number 2.

  1. Dispatch with delegate
Func<string> hello = () => "Hello World";
RequestDelegate rd1 = RequestDelegateFa.Build(hello);
app.Run(rd1);

RequestDelegate rd2 = RequestDelegateFactory.Create(Func<string>)MyClass.Hello);
app.Run(rd2);

public class MyClass
{
    public static string Hello() => "Hello World";
}

2a. Class dispatch with instance

MethodInfo methodInfo = typeof(MyClass).GetMethod(nameof(MyClass.Hello));

// This will create an instance of MyClass per request, giving the factory a chance to construct
// MyClass using per request scoped objects
RequestDelegate rd1 = RequestDelegateFactory.Create(methodInfo, context => new MyClass());

// Example with DI
RequestDelegate rd2 = RequestDelegateFactory.Create(methodInfo, context => context.RequestServices.GetRequiredService<MyClass>());

app.Run(rd1);
app.Run(rd2);

public class MyClass
{
    public string Hello() => "Hello World";
}

2a. Class dispatch with static

MethodInfo methodInfo = typeof(MyClass).GetMethod(nameof(MyClass.Hello));

RequestDelegate rd = RequestDelegateFactory.Create(methodInfo);

app.Run(rd);

public class MyClass
{
    public static string Hello() => "Hello World";
}

Alternatives

Don't expose this building block and expose it on more places. Middleware, Routing, IApplicationBuilder etc.

PS: We might also need to consider options that control behavior in the future.

cc @pranavkm

Metadata

Metadata

Assignees

Labels

DoneThis issue has been fixedapi-approvedAPI was approved in API review, it can be implementedarea-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcenhancementThis issue represents an ask for new feature or an enhancement to an existing onefeature-minimal-actionsController-like actions for endpoint routing

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions