RequestDelegate
上次,我们在《如何判断当前请求的API类型》中查看endpoints.MapControllers()
实现时,最终定位到ActionEndpointFactory.cs,其中有这样一段代码:
private static RequestDelegate CreateRequestDelegate()
{// We don't want to close over the Invoker Factory in ActionEndpointFactory as// that creates cycles in DI. Since we're creating this delegate at startup time// we don't want to create all of the things we use at runtime until the action// actually matches.//// The request delegate is already a closure here because we close over// the action descriptor.IActionInvokerFactory? invokerFactory = null;return (context) =>{var endpoint = context.GetEndpoint()!;var dataTokens = endpoint.Metadata.GetMetadata<IDataTokensMetadata>();var routeData = new RouteData();routeData.PushState(router: null, context.Request.RouteValues, new RouteValueDictionary(dataTokens?.DataTokens));// Don't close over the ActionDescriptor, that's not valid for pages.var action = endpoint.Metadata.GetMetadata<ActionDescriptor>()!;var actionContext = new ActionContext(context, routeData, action);if (invokerFactory == null){invokerFactory = context.RequestServices.GetRequiredService<IActionInvokerFactory>();}var invoker = invokerFactory.CreateInvoker(actionContext);return invoker!.InvokeAsync();};
}
从代码上理解,应该是执行请求时,会创建IActionInvokerFactory
实例,由它创建 invoker 执行。
是不是这样呢,我们验证一下!
IActionInvokerFactory
新建CustomActionInvokerFactory.cs
,继承IActionInvokerFactory,实现代码如下:
public class CustomActionInvokerFactory : IActionInvokerFactory
{private readonly IActionInvokerProvider[] _actionInvokerProviders;public ActionInvokerFactory(IEnumerable<IActionInvokerProvider> actionInvokerProviders){_actionInvokerProviders = actionInvokerProviders.OrderBy(item => item.Order).ToArray();}public IActionInvoker? CreateInvoker(ActionContext actionContext){var context = new ActionInvokerProviderContext(actionContext);foreach (var provider in _actionInvokerProviders){provider.OnProvidersExecuting(context);}for (var i = _actionInvokerProviders.Length - 1; i >= 0; i--){_actionInvokerProviders[i].OnProvidersExecuted(context);}return context.Result;}
}
代码 Copy 自 ASP.NET Core 内部实现类ActionInvokerFactory。
然后在 Startup.cs 注册实现:
public void ConfigureServices(IServiceCollection services)
{services.AddSingleton<IActionInvokerFactory, CustomActionInvokerFactory>();...
}
打上断点,执行API,发现确实如设想中一样,请求时执行CreateInvoker
方法:
在其中发现了一个很有意思的属性MethodInfo
,正是对应我们执行的 Action 方法。
突发奇想,如果我们替换MethodInfo
属性,是不是会执行其他方法呢?
Demo
创建 WeatherForecast2Controller,实现代码如下:
[ApiController]
[Route("[controller]")]
public class WeatherForecast2Controller : ControllerBase
{[HttpGet]public string Get2(){return "My IO";}
}
可以看到这是和原方法完全不同的实现。
现在进行替换:
var actionDescriptor = actionContext.ActionDescriptor as ControllerActionDescriptor;
actionDescriptor.MethodInfo = typeof(WeatherForecast2Controller).GetMethod("Get2");
执行,出现错误提示:
查看ControllerActionInvokerCache.cs
实现:
var objectMethodExecutor = ObjectMethodExecutor.Create(actionDescriptor.MethodInfo,actionDescriptor.ControllerTypeInfo,parameterDefaultValues);
原来,还要替换actionDescriptor.ControllerTypeInfo
:
var actionDescriptor = actionContext.ActionDescriptor as ControllerActionDescriptor;
actionDescriptor.MethodInfo = typeof(WeatherForecast2Controller).GetMethod("Get2");
actionDescriptor.ControllerTypeInfo = typeof(WeatherForecast2Controller).GetTypeInfo();
再次运行,执行成功!
结论
替换 Action 实际执行方法,最好的使用场景是定制化开发,比如客户需求和产品实现完全不同,可以保证请求不变的情况下执行客户定制化实现。
想了解更多内容,请关注我的个人公众号”My IO“