自动挡换手动挡:在 ASP.NET Core 3.0 Middleware 中手动执行 Co
由于遭遇 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 方法,才有了参考。 private static ControllerActionDescriptor CreateActionDescriptor(...) { var actionDescriptor = 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); return actionDescriptor; } 带上参考小手册,开始试驾。。。经过无数次熄火(NullReferenceException) 后,总算用手动挡开车上路,于是就有了这篇随笔分享手动挡驾驶小经验。 手动挡的操作杆主要有:RouteData,?ActionDescriptor,?ActionContext,?ActionInvokerFactory,?ControllerActionInvoker 其中最难操作的也是最重要的是?ActionDescriptor ,绝大多数的熄火都是在操作它时发生的,它有8个属性需要赋值,有些属性即使没用到也要进行初始化赋值,不然立马熄火(null引用异常)。 ActionDescriptor 的操作方法如下 var actionDesciptor = new ControllerActionDescriptor() { ControllerName = controllerType.Name,ActionName = actionName,FilterDescriptors = new List<FilterDescriptor>(),MethodInfo = typeof(HomeController).GetMethod(actionName,BindingFlags.Public | BindingFlags.Instance),ControllerTypeInfo = controllerType.GetTypeInfo(),Parameters = new List<ParameterDescriptor>(),Properties = new Dictionary<object,object>(),BoundProperties = 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 foreach (var routeValue in routeData.Values) { var parameter = new ParameterDescriptor(); parameter.Name = routeValue.Key; var attributes = new object[] { new FromRouteAttribute { Name = parameter.Name },}; parameter.BindingInfo = BindingInfo.GetBindingInfo(attributes); parameter.ParameterType = routeValue.Value.GetType(); actionDesciptor.Parameters.Add(parameter); } 有了?ActionDescriptor 与?RouteData 之后,只需4步操作,可以把车开起来了。 var actionContext = new ActionContext(context,routeData,actionDesciptor); var actionInvokerFactory = app.ApplicationServices.GetRequiredService<IActionInvokerFactory>(); //ActionInvokerFactory var invoker = actionInvokerFactory.CreateInvoker(actionContext); //ControllerActionInvoker await invoker.InvokeAsync(); 但车没有跑在高速上,而是通过 ASP.NET Core 3.0 的 EndpointRouting 跑在了 middleware 中。 app.UseEndpoints(endpoints => { endpoints.MapGet("/",async context => { var routeData = new RouteData(); routeData.Values.Add("message","Hello World!"); await DriveControllerAction(context,app); }); }); 看看手动挡开车的效果,Contorller 的示例代码如下 public class HomeController : Controller { public IActionResult Index(string message) { ViewBag.Message = message; return View(); } } 运行结果 手动挡驾驶 ASP.NET Core 3.0 Preview 5 版 Contoller Action 型新车成功! 完整代码见 github 上的 Startup.cs? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net – 如何在标记后继续内联任何标记
- asp.net – Team Build 2010和web.config转换的问题
- ASP.NET MVC输出缓存为多应用程序,因主机名和文化而异
- 如何处理在MVC视图中应用程序启动和传输和显示错误发生的AS
- asp.net-mvc – asp.net mvc中editortemplate中复杂类型的M
- asp.net – 在Web应用程序中使用声明进行身份验证和授权的实
- asp.net-mvc – 与ASP.NET MVC等效的GetWebResourceUrl?
- asp.net-mvc – 我如何可以渲染局部视图在asp.net mvc 3
- IIS反向代理不使用ASP.NET中的Response.Redirect()
- asp.net – 如何从CLASS(Inside Class)调用ASPX页面
- asp.net – MVC 4如何为WebGrid设置行ID
- asp.net-mvc – 在Controller或其他地方渲染部分
- 获取ASP.NET中所有活动会话的列表
- asp.net-mvc-2 – MVC2 – > MVC3升级
- asp.net – 如何确保使用后无法恢复上传的文件内
- asp.net – 如何通过sqldatasource计算获取的行数
- 经典asp JScript中的重载函数
- ASP.NET MVC 2.0 – RenderPartial和RenderActio
- asp.net-mvc – 神秘的ASP.NET MVC Action高延迟
- asp.net – Session不会保留值并始终返回null