ASP.NET Core 替换 Action 实际执行方法
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“
總結
以上是生活随笔為你收集整理的ASP.NET Core 替换 Action 实际执行方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#中的表达式和运算符
- 下一篇: .NET6之MiniAPI(十七):缓存