.net core 源码解析-mvc route的注册,激活,调用流程(三)

.net core mvc route的注册,激活,调用流程

mvc的入口是route,当前请求的url匹配到合适的route之后,mvc根据route所指定的controller和action激活controller并调用action完成mvc的处理流程。下面我们看看服务器是如何调用route的。
core mvc startup基本代码。重点在AddMvc和UseMvc

public class Startup{    
   
   public
IConfigurationRoot Configuration { get; }    // This method gets called by the runtime. Use this method to add services to the container.    public void ConfigureServices(IServiceCollection services)    {        services.AddMvc();    }    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)    {        app.UseStaticFiles();        app.UseMvc(routes =>        {            routes.MapRoute(                name: "default",                template: "{controller=Home}/{action=Index}/{id?}");        });    } }

AddMvc:把各种service加入IOC容器。比如格式化提供程序,action定位器,controllerFactory,controller激活器等等,一应服务全部在这里加入。
UseMvc:最重要的一行代码:builder.UseMiddleware (router); 看到这行代码就清楚的知道route 这个handler 在这里加入到请求委托链拉

public static IMvcBuilder AddMvc(this IServiceCollection services){    var builder = services.AddMvcCore();
    builder.AddJsonFormatters();
    builder.AddCors();    return new MvcBuilder(builder.Services, builder.PartManager);
}public static IMvcCoreBuilder AddMvcCore(this IServiceCollection services, Action<MvcOptions> setupAction){    var builder = services.AddMvcCore();
    services.Configure(setupAction);    return builder;
}internal static void AddMvcCoreServices(IServiceCollection services){
    services.TryAddSingleton<IActionSelector, ActionSelector>();
    services.TryAddSingleton<ActionConstraintCache>();
    services.TryAddSingleton<IActionSelectorDecisionTreeProvider, ActionSelectorDecisionTreeProvider>();    // This has a cache, so it needs to be a singleton
    services.TryAddSingleton<IControllerFactory, DefaultControllerFactory>();    // Will be cached by the DefaultControllerFactory
    services.TryAddTransient<IControllerActivator, DefaultControllerActivator>();
    services.TryAddEnumerable(ServiceDescriptor.Transient<IControllerPropertyActivator, DefaultControllerPropertyActivator>());    // Route Handlers
    services.TryAddSingleton<MvcRouteHandler>(); // Only one per app
    services.TryAddTransient<MvcAttributeRouteHandler>(); // Many per app}public static IApplicationBuilder UseMvc(this IApplicationBuilder app, Action<IRouteBuilder> configureRoutes){    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());
}public static IApplicationBuilder UseRouter(this IApplicationBuilder builder, IRouter router){    return builder.UseMiddleware<RouterMiddleware>(router);
}

如此,mvc的入口route handler就加入了我们的请求委托链中。后续服务器接收到的请求就能交由route匹配,查找action,激活action处理。

router middleware的激活调用

middleware 请求调用委托链的激活调用请看这篇文章

//middleware加入_components请求处理委托链
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware){    _components.Add(middleware);  
 return this; }
 
 public static class UseMiddlewareExtensions{  
 
   private const string InvokeMethodName = "Invoke";
      private static readonly MethodInfo GetServiceInfo = typeof(UseMiddlewareExtensions).GetMethod(nameof(GetService), BindingFlags.NonPublic | BindingFlags.Static);    //注册middleware    

   public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Type middleware, params object[] args)
   
{      
       var applicationServices = app.ApplicationServices;        //将middleware 加入请求处理委托链        return app.Use(next =>        {        //解析方法和参数。查找类的Invoke方法作为入口方法。所以middleware只要是个class就行。只要有一个功公共的Invoke方法即可。            var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public);          
         var invokeMethods = methods.Where(m => string.Equals(m.Name, InvokeMethodName, StringComparison.Ordinal)).ToArray();    
         var methodinfo = invokeMethods[0];    
         var parameters = methodinfo.GetParameters();  
         var ctorArgs = new object[args.Length + 1];            ctorArgs[0] = next;            Array.Copy(args, 0, ctorArgs, 1, args.Length);  
         //创建middleware的实例。并且通过构造函数注入相关的service            var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs);            //如果方法只有一个参数,默认它就是httpcontext。            if (parameters.Length == 1)                {                  
           return (RequestDelegate)methodinfo.CreateDelegate(typeof(RequestDelegate), instance);                }          
             //多余一个参数的则构建一个func。并从ioc容器解析参数注入            var factory = Compile<object>(methodinfo, parameters);            return context =>            {              
              var serviceProvider = context.RequestServices ?? applicationServices;              
               return factory(instance, context, serviceProvider);            };        });    }//代码中的创建实例注入service,创建有多个参数的invoke方法注入service具体代码就不贴上来了,占地方。//构造函数就是匹配最适合的构造函数,然后从IServiceProvider get实例,注入。//多个参数的invoke就更简单了。直接从IServiceProvider get实例注入。

上述源代码git地址,aspnet/HttpAbstractions项目

route handler middleware代码

public class RouterMiddleware{  
   private readonly ILogger _logger;  
   private readonly RequestDelegate _next;
   private readonly IRouter _router;    //创建middleware的实例。并且通过构造函数注入相关的service    public RouterMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IRouter router)    {        _next = next;        _router = router;        _logger = loggerFactory.CreateLogger<RouterMiddleware>();    }    //被调用的方法。从这里开始进入mvc route。    public async Task Invoke(HttpContext httpContext)    {  
        //此处的 IRouter router对象。是我们在Startup中routes.MapRoute...配置的route集合对象:RouteCollection。当然也还有比如attributeroute等等好几种route。                var context = new RouteContext(httpContext);        context.RouteData.Routers.Add(_router);                await _router.RouteAsync(context);      
          if (context.Handler == null)        {        //没有匹配到route的情况            _logger.RequestDidNotMatchRoutes();      
               await _next.Invoke(httpContext);        }    
        else        {            httpContext.Features[typeof(IRoutingFeature)] = new RoutingFeature()            {                RouteData = context.RouteData,            };            //匹配到路由处理            await context.Handler(context.HttpContext);        }    } }
//Microsoft.AspNetCore.Routing.RouteCollection
public async virtual Task RouteAsync(RouteContext context){  
 // Perf: We want to avoid allocating a new RouteData for each route we need to process.    // We can do this by snapshotting the state at the beginning and then restoring it    // for each router we execute.    var snapshot = context.RouteData.PushState(null, values: null, dataTokens: null);  
  for (var i = 0; i < Count; i++)    {      
      var route = this[i];        context.RouteData.Routers.Add(route);    
       try        {          //循环所有routes规则,逐一匹配,匹配到一个自然就break。            await route.RouteAsync(context);        
           if (context.Handler != null)            
               break;        }      
        finally        {            if (context.Handler == null)                snapshot.Restore();        }    } }

UseMvc中有一行非常重要的代码。给RouteBuilder的DefaultHandler赋值一个handler。记住这行代码,我们继续往下看。

public static IApplicationBuilder UseMvc(this IApplicationBuilder app, Action<IRouteBuilder> configureRoutes){   
 var routes = new RouteBuilder(app)    {        DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(),    }; }
 //我们在Startup中routes.MapRoute的所有调用最终调用方法都是这个。new Route( routeBuilder.DefaultHandler,....)//全部都指定了_target为routeBuilder.DefaultHandler

public static IRouteBuilder MapRoute(this IRouteBuilder routeBuilder, string name, string template, object defaults, object constraints, object dataTokens){  
  if (routeBuilder.DefaultHandler == null)  
        throw new RouteCreationException(Resources.FormatDefaultHandler_MustBeSet(nameof(IRouteBuilder)));  
         var inlineConstraintResolver = routeBuilder.ServiceProvider.GetRequiredService<IInlineConstraintResolver>();    routeBuilder.Routes.Add(new Route(        routeBuilder.DefaultHandler,        name, template, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints), new RouteValueDictionary(dataTokens), inlineConstraintResolver));  
          return routeBuilder; }

到这里,我们的逻辑有点绕了,让我们理理清楚:
1.请求进到RouterMiddleware.Invoke()方法
2.调用RouteCollection.RouteAsync()方法,RouteCollection.RouteAsync方法中循环注册的每一个route对象。
并调用route对象的RouteAsync()方法(route对象的RouteAsync方法在它的父类中Microsoft.AspNetCore.Routing.RouteBase)。
这里说的route对象即时Startup中routes.MapRoute生成的route对象(Microsoft.AspNetCore.Routing.Route)。Route继承RouteBase,RouteBase实现IRouter接口
3.RouteBase.RouteAsync()判断当前请求是否符合当前route规则,如果匹配的话,则调用抽象方法OnRouteMatched
4.RouteBase的抽象方法OnRouteMatched,又回到Route对象的OnRouteMatched方法中。调用_target.RouteAsync();_target对象即上面代码中的routeBuilder.DefaultHandler。
5.来到Microsoft.AspNetCore.Mvc.Internal.MvcRouteHandler.RouteAsync()方法中。最重要的一行代码: context.Handler =....
6.调用堆栈最终返回到1中(RouterMiddleware.Invoke())。判断context.Handler == null。为null没找到route匹配的action。不为null则await context.Handler(context.HttpContext)
7.context.Handler即为5中赋值的func。即下面的代码,定位action,调用action。

//Microsoft.AspNetCore.Mvc.Internal.MvcRouteHandler.RouteAsync
public Task RouteAsync(RouteContext context){  
 var candidates = _actionSelector.SelectCandidates(context);  
  if (candidates == null || candidates.Count == 0)    {        _logger.NoActionsMatched();      
    return TaskCache.CompletedTask;    }    
    var actionDescriptor = _actionSelector.SelectBestCandidate(context, candidates);  
    if (actionDescriptor == null)    {        _logger.NoActionsMatched();    
       return TaskCache.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 TaskCache.CompletedTask; }

至此,route的处理流程大约交代清楚了。包括route的注册,route的激活,route的选择等。

相关文章: 

原文地址:http://www.cnblogs.com/calvinK/p/6226219.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值