加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

自动挡换手动挡:在 ASP.NET Core 3.0 Middleware 中手动执行 Co

发布时间:2020-12-16 07:11:35 所属栏目:asp.Net 来源:网络整理
导读:由于遭遇 System.Data.SqlClient 的性能问题(详见之前的博文),向 .NET Core 3.0 的升级工作被迫提前了。在升级过程中遇到了一个问题,我们在?Razor Class Library 中实现的自定义错误页面无法在 ASP.NET Core 3.0 Preview 5 中正常工作,问题原因详见博问

由于遭遇 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?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读