ASP.NET Core MVC — MVC执行概貌(0) 路由与MVC的执行流程

许多中间件都会为IApplicationBuilder添加一个扩展方法以简化调用,比如在2.x中常见的UseRoute()之类,其实就是简单调用UseMiddleware<Xxx>()。在ASP.NET CoreMVC也同样提供了一个UseMvc()扩展方法,但是和会转换为UseMiddleware<XMiddleware>()的那些方法不同,并没有一个与UseMvc()对应的、名称为MvcMiddleware的中间件存在。

其实,所谓的UseMvc()其实不过是对UseRouter()的封装:

// project: 
//      aspnet/Mvc
// file:
//      src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcApplicationBuilderExtensions.cs
public static IApplicationBuilder UseMvc( this IApplicationBuilder app, Action<IRouteBuilder> configureRoutes)
{
    if (app == null){ throw new ArgumentNullException(nameof(app)); }
    if (configureRoutes == null){ throw new ArgumentNullException(nameof(configureRoutes)); }
    // throw an exception if there's no MvcMarkerService 
    VerifyMvcIsRegistered(app);   
    var routes = new RouteBuilder(app)
    {
        DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(),
    };
    configureRoutes(routes);
    routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices));
    return app.UseRouter(routes.Build());
}

可以看到,执行app.UseMvc(rb=>{ /**/ }) 之类的操作,其实是在配置一个路由中间件,其默认处理器是MvcRouterHandler的实例。 这个MvcRouterHandler继承自IRouter,核心思想便是先进行MVC匹配,如果匹配不成功,则返回Task.CompletedTask(根据路由中间件的实现原理,之后会交由后续中间件进行处理);否则,获取路由匹配到的数据,设置处理器为调用相关动作。源码如下:

 public class MvcRouteHandler : IRouter{
    private readonly IActionContextAccessor _actionContextAccessor;
    private readonly IActionInvokerFactory _actionInvokerFactory;
    private readonly IActionSelector _actionSelector;
    private readonly ILogger _logger;
    private readonly DiagnosticSource _diagnosticSource;
     
    // ...
    
    public Task RouteAsync(RouteContext context)
    {
        if (context == null) { throw new ArgumentNullException(nameof(context)); }
    
        var candidates = _actionSelector.SelectCandidates(context);
        if (candidates == null || candidates.Count == 0)
        {
            _logger.NoActionsMatched(context.RouteData.Values);
            return Task.CompletedTask;
        }
    
        var actionDescriptor = _actionSelector.SelectBestCandidate(context, candidates);
        if (actionDescriptor == null)
        {
            _logger.NoActionsMatched(context.RouteData.Values);
            return Task.CompletedTask;
        }
    
        context.Handler = (c) =>
        {
            var routeData = c.GetRouteData();
    
            var actionContext = new ActionContext(context.HttpContext, routeData, actionDescriptor);
            if (_actionContextAccessor != null)
            {
                _actionContextAccessor.ActionContext = actionContext;
            }
    
            var invoker = _actionInvokerFactory.CreateInvoker(actionContext);
            if (invoker == null)
            {
                throw new InvalidOperationException(
                    Resources.FormatActionInvokerFactory_CouldNotCreateInvoker(
                        actionDescriptor.DisplayName));
            }
    
            return invoker.InvokeAsync();
        };
    
        return Task.CompletedTask;
    }
    
    // ...
}

总结来说,这里的代码主要完成以下工作:

  1. 这里一旦路由匹配,则可以得知当前路由所命中ActionASP.NET Core使用ActionDescriptor描述该Action。其中包含了当前Action方法名、参数描述符表、过滤器描述表、Action约束、RouteValue等信息。

  2. 作为一个IRouter,路由匹配后自然还要设置Handler以示命中:这里也就是构建一个routeContext.Handler。该Handler执行以下基本处理过程:

    1. 根据当前HttpContext,配合当前路由数据、及所命中ActionActionDescriptor,构建上下文ActionContext:
      var actionContext = new ActionContext(httpContext, routeData, actionDescriptor)
    
    1. 根据ActionContext,创建IActionInvoker
    2. 执行IActionInvoker.InvokeAsync(),触发 MVC pipeline的调用
      具体的细节,比如IActionInvoker是如何创建的,在后续相关笔记中记录。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值