zoukankan      html  css  js  c++  java
  • .NET Core开发日志——简述路由

    有过ASP.NET或其它现代Web框架开发经历的开发者对路由这一名字应该不陌生。如果要用一句话解释什么是路由,可以这样形容:通过对URL的解析,指定相应的处理程序。

    回忆下在Web Forms应用程序中使用路由的方式:

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.MapPageRoute("",
            "Category/{action}/{categoryName}",
            "~/categoriespage.aspx");
    }
    

    然后是MVC应用程序:

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
        routes.MapRoute(
            "Default",                                              
            "{controller}/{action}/{id}",                          
            new { controller = "Home", action = "Index", id = "" }  
        );
    }
    

    再到了ASP.NET Core:

    public void Configure(IApplicationBuilder app)
    {
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
    

    还可以用更简单的写法:

    public void Configure(IApplicationBuilder app)
    {
        app.UseMvcWithDefaultRoute();
    }
    

    从源码上看这两个方法的实现是一样的。

    public static IApplicationBuilder UseMvcWithDefaultRoute(this IApplicationBuilder app)
    {
        if (app == null)
        {
            throw new ArgumentNullException(nameof(app));
        }
    
        return app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
    

    关键是内部UseMvc方法的内容:

    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());
    }
    

    其中的处理过程,首先实例化了一个RouteBuilder对象,并对它的DefaultHandler属性赋值为MvcRouteHandler。接着以其为参数,执行routes.MapRoute方法。

    MapRoute的处理过程就是为RouteBuilder里的Routes集合新增一个Route对象。

    public static IRouteBuilder MapRoute(
        this IRouteBuilder routeBuilder,
        string name,
        string template,
        object defaults,
        object constraints,
        object dataTokens)
    {
        ...
    
        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;
    }
    

    有此一个Route对象仍不夠,程序里又插入了一个AttributeRoute。

    随后执行routes.Build(),返回RouteCollection集合。该集合实现了IRouter接口。

    public IRouter Build()
    {
        var routeCollection = new RouteCollection();
    
        foreach (var route in Routes)
        {
            routeCollection.Add(route);
        }
    
        return routeCollection;
    }
    

    最终使用已完成配置的路由。

    public static IApplicationBuilder UseRouter(this IApplicationBuilder builder, IRouter router)
    {
        ...
    
        return builder.UseMiddleware<RouterMiddleware>(router);
    }
    

    于是又看到了熟悉的Middleware。它的核心方法里先调用了RouteCollection的RouteAsync处理。

    public async Task Invoke(HttpContext httpContext)
    {
        var context = new RouteContext(httpContext);
        context.RouteData.Routers.Add(_router);
    
        await _router.RouteAsync(context);
    
        if (context.Handler == null)
        {
            _logger.RequestDidNotMatchRoutes();
            await _next.Invoke(httpContext);
        }
        else
        {
            httpContext.Features[typeof(IRoutingFeature)] = new RoutingFeature()
            {
                RouteData = context.RouteData,
            };
    
            await context.Handler(context.HttpContext);
        }
    }
    

    其内部又依次执行各个Route的RouteAsync方法。

    public async virtual Task RouteAsync(RouteContext context)
    {
        ...
    
        for (var i = 0; i < Count; i++)
        {
            var route = this[i];
            context.RouteData.Routers.Add(route);
    
            try
            {
                await route.RouteAsync(context);
    
                if (context.Handler != null)
                {
                    break;
                }
            }
            ...
        }
    }
    

    之前的逻辑中分别在RouteCollection里加入了AttributeRoute与Route。
    *循环中会判断Handler是否被赋值,这是为了避免在路由已被匹配的情况下,继续进行其它的匹配。从执行顺序来看,很容易明白AttributeRoute比一般Route优先级高的道理。

    先执行AttributeRoute里的RouteAsync方法:

    public Task RouteAsync(RouteContext context)
    {
        var router = GetTreeRouter();
        return router.RouteAsync(context);
    }
    

    里面调用了TreeRouter的RouteAsync方法:

    public async Task RouteAsync(RouteContext context)
    {
        foreach (var tree in _trees)
        {
            var tokenizer = new PathTokenizer(context.HttpContext.Request.Path);
            var root = tree.Root;
    
            var treeEnumerator = new TreeEnumerator(root, tokenizer);
    
            ...
    
            while (treeEnumerator.MoveNext())
            {
                var node = treeEnumerator.Current;
                foreach (var item in node.Matches)
                {
                    var entry = item.Entry;
                    var matcher = item.TemplateMatcher;
    
                    try
                    {
                        if (!matcher.TryMatch(context.HttpContext.Request.Path, context.RouteData.Values))
                        {
                            continue;
                        }
    
                        if (!RouteConstraintMatcher.Match(
                            entry.Constraints,
                            context.RouteData.Values,
                            context.HttpContext,
                            this,
                            RouteDirection.IncomingRequest,
                            _constraintLogger))
                        {
                            continue;
                        }
    
                        _logger.MatchedRoute(entry.RouteName, entry.RouteTemplate.TemplateText);
                        context.RouteData.Routers.Add(entry.Handler);
    
                        await entry.Handler.RouteAsync(context);
                        if (context.Handler != null)
                        {
                            return;
                        }
                    }
                    ...
                }
            }
        }
    }
    

    如果所有AttributeRoute路由都不能匹配,则不会进一步作处理。否则的话,将继续执行Handler中的RouteAsync方法。这里的Handler是MvcAttributeRouteHandler。

    public Task RouteAsync(RouteContext context)
    {
        ...
    
        var actionDescriptor = _actionSelector.SelectBestCandidate(context, Actions);
        if (actionDescriptor == null)
        {
            _logger.NoActionsMatched(context.RouteData.Values);
            return Task.CompletedTask;
        }
    
        foreach (var kvp in actionDescriptor.RouteValues)
        {
            if (!string.IsNullOrEmpty(kvp.Value))
            {
                context.RouteData.Values[kvp.Key] = kvp.Value;
            }
        }
    
        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;
    }
    

    该方法内部的处理仅是为RouteContext的Handler属性赋值。实际的操作则是要到RouterMiddleware中Invoke方法的context.Handler(context.HttpContext)这一步才被执行的。

    至于Route里的RouteAsync方法:

    public virtual Task RouteAsync(RouteContext context)
    {
        ...
    
        EnsureMatcher();
        EnsureLoggers(context.HttpContext);
    
        var requestPath = context.HttpContext.Request.Path;
    
        if (!_matcher.TryMatch(requestPath, context.RouteData.Values))
        {
            // If we got back a null value set, that means the URI did not match
            return Task.CompletedTask;
        }
    
        // Perf: Avoid accessing dictionaries if you don't need to write to them, these dictionaries are all
        // created lazily.
        if (DataTokens.Count > 0)
        {
            MergeValues(context.RouteData.DataTokens, DataTokens);
        }
    
        if (!RouteConstraintMatcher.Match(
            Constraints,
            context.RouteData.Values,
            context.HttpContext,
            this,
            RouteDirection.IncomingRequest,
            _constraintLogger))
        {
            return Task.CompletedTask;
        }
        _logger.MatchedRoute(Name, ParsedTemplate.TemplateText);
    
        return OnRouteMatched(context);
    }
    

    只有路由被匹配的时候才在OnRouteMatched里调用target的RouteAsync方法。

    protected override Task OnRouteMatched(RouteContext context)
    {
        context.RouteData.Routers.Add(_target);
        return _target.RouteAsync(context);
    }
    

    此处的target即是最初创建RouteBuilder时传入的MvcRouteHandler。

    public Task RouteAsync(RouteContext 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;
    }
    

    处理过程与MvcAttributeRouteHandler相似,一样是要在RouterMiddleware的Invoke里才执行Handler的方法。

    以一张思维导图可以简单概括上述的过程。

    或者用三句话也可以描述整个流程。

    • 添加路由
    • 匹配地址
    • 处理请求
  • 相关阅读:
    C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!
    Java8的新特性以及与C#的比较
    点旋转坐标变换
    vc++返回模块路径
    为什么不要 "lock(this)" ? lock object 并是readonly(转载)
    《黄帝内经》要义
    C++多线程编程简单实例
    c++ 获取文件大小
    c# 获取文件夹大小
    自动驾驶仿真工具的下载与安装
  • 原文地址:https://www.cnblogs.com/kenwoo/p/9484499.html
Copyright © 2011-2022 走看看