由于遭遇 System.Data.sqlClient 的性能问题(详见之前的博文),向 .NET Core 3.0 的升级工作被迫提前了。在升级过程中遇到了一个问题,我们在 Razor Class Library 中实现的自定义错误页面无法在 ASP.NET Core 3.0 Preview 5 中正常工作,问题原因详见博问 Razor Class Library 中的属性路由在 ASP.NET Core 3.0 中不起作用 。
由于属性路由不起作用的问题没找到解决方法,于是被迫采用了另外一种解决方法 —— 在中间件中调用 Razor Class Library 中的 Controller Action 显示自定义错误页面。这就需要将原先由 ASP.NET Core Runtime 自动执行的 Controller Action (自动挡)改为手工执行(手动挡),之前没玩过,借此机会试一试。
不试不知道,一试吓一跳,手动操作好麻烦,这不是自动挡换手动挡,这是自动挡换拖拉机。
开始寸步难行,挂挡都不知道在哪挂,后来在 ASP.NET Core 3.0 的源码中找到了 ControllerActionDescriptorBuilder.cs 中的 CreateActionDescriptor 方法,才有了参考。
@H_502_14@private @H_502_14@static ControllerActionDescriptor CreateActionDescriptor(...) { @H_502_14@var actionDescriptor = @H_502_14@new ControllerActionDescriptor { ActionName = action.ActionName,MethodInfo = action.ActionMethod,}; actionDescriptor.ControllerName = controller.ControllerName; actionDescriptor.ControllerTypeInfo = controller.ControllerType; AddControllerPropertyDescriptors(actionDescriptor,controller); AddActionConstraints(actionDescriptor,selector); AddEndpointMetadata(actionDescriptor,selector); AddAttributeRoute(actionDescriptor,selector); AddParameterDescriptors(actionDescriptor,action); AddActionFilters(actionDescriptor,action.Filters,controller.Filters,application.Filters); AddApiExplorerInfo(actionDescriptor,application,controller,action); AddRouteValues(actionDescriptor,action); AddProperties(actionDescriptor,action,application); @H_502_14@return actionDescriptor; }
带上参考小手册,开始试驾。。。经过无数次熄火(NullReferenceException) 后,总算用手动挡开车上路,于是就有了这篇随笔分享手动挡驾驶小经验。
手动挡的操作杆主要有:RouteData, ActionDescriptor, ActionContext, ActionInvokerFactory, ControllerActionInvoker
其中最难操作的也是最重要的是 ActionDescriptor ,绝大多数的熄火都是在操作它时发生的,它有8个属性需要赋值,有些属性即使没用到也要进行初始化赋值,不然立马熄火(null引用异常)。
ActionDescriptor 的操作方法如下
@H_502_14@var actionDesciptor = @H_502_14@new ControllerActionDescriptor() { ControllerName = controllerType.Name,ActionName = actionName,FilterDescriptors = @H_502_14@new List<FilterDescriptor>(),MethodInfo = @H_502_14@typeof(HomeController).getmethod(actionName,BindingFlags.Public | BindingFlags.Instance),ControllerTypeInfo = controllerType.GetTypeInfo(),Parameters = @H_502_14@new List<ParameterDescriptor>(),Properties = @H_502_14@new Dictionary<@H_502_14@object,@H_502_14@object>(),BoundProperties = @H_502_14@new List<ParameterDescriptor>() };
ControllerActionDescriptor 继承自 ActionDescriptor ,上面的赋值操作中真正传递有价值数据的是 ControllerName, ActionName,MethodInfo, ControllerTypeInfo 。一开始不知道要对哪些属性赋值,只能一步一步试,根据熄火情况一个一个添加,最终得到了上面的最少赋值操作。
第二重要的是 RouteData ,它是数据传输带,不仅要通过它向 ActionDescriptor 传送 BindingInfo 以及 Action 方法通过它获取参数值,而且要向视图引擎(比如ViewEngineResult,ViewResultExecutor)传送 controller 与 action 的名称,不然视图引擎找不到视图文件。
RouteData 的操作方法如下
//For searching View routeData.Values.Add("controller",actionDesciptor.ControllerName.Replace("Controller","")); routeData.Values.Add("action",actionDesciptor.ActionName); //For binding action parameters @H_502_14@foreach (@H_502_14@var routeValue @H_502_14@in routeData.Values) { @H_502_14@var parameter = @H_502_14@new ParameterDescriptor(); parameter.Name = routeValue.Key; @H_502_14@var attributes = @H_502_14@new @H_502_14@object[] { @H_502_14@new FromrouteAttribute { Name = parameter.Name },}; parameter.BindingInfo = BindingInfo.GetBindingInfo(attributes); parameter.ParameterType = routeValue.Value.GetType(); actionDesciptor.Parameters.Add(parameter); }
有了 ActionDescriptor 与 RouteData 之后,只需4步操作,可以把车开起来了。
@H_502_14@var actionContext = @H_502_14@new ActionContext(context,routeData,actionDesciptor); @H_502_14@var actionInvokerFactory = app.applicationservices.GetrequiredService<IActionInvokerFactory>(); //ActionInvokerFactory @H_502_14@var invoker = actionInvokerFactory.CreateInvoker(actionContext); //ControllerActionInvoker @H_502_14@await invoker.InvokeAsync();
但车没有跑在高速上,而是通过 ASP.NET Core 3.0 的 EndpointRouting 跑在了 middleware 中。
app.UseEndpoints(endpoints => { endpoints.MapGet("/",@H_502_14@async context => { @H_502_14@var routeData = @H_502_14@new RouteData(); routeData.Values.Add("message","Hello World!"); @H_502_14@await DriveControllerAction(context,app); }); });
@H_502_14@public @H_502_14@class HomeController : Controller { @H_502_14@public IActionResult Index(@H_502_14@string message) { ViewBag.Message = message; @H_502_14@return View(); } }
运行结果
手动挡驾驶 ASP.NET Core 3.0 Preview 5 版 Contoller Action 型新车成功!
完整代码见 github 上的 Startup.cs
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。