zoukankan      html  css  js  c++  java
  • .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的选择等。

  • 相关阅读:
    在 ubuntu20 上安装 docker
    在 ubuntu20 上替换原有的源,解决下载软件慢的问题
    thymeleaf 模板语法
    对 spring 中默认的 DataSource 创建进行覆盖
    Spring Security OAuth2 笔记(一)
    对 ThreadLocal 的了解(一)
    解决 docker.io 上拉取 images Get https://registry-1.docker.io/v2/: net/http: TLS handshake timeout
    Spring Security Oauth2 使用 token 访问资源服务器出现异常:Invalid token does not contain resource id (oauth2)
    windows 查看被占用的端口信息
    不写注释的程序员-Models
  • 原文地址:https://www.cnblogs.com/calvinK/p/6226219.html
Copyright © 2011-2022 走看看