有过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的方法。
以一张思维导图可以简单概括上述的过程。
或者用三句话也可以描述整个流程。
- 添加路由
- 匹配地址
- 处理请求
.NET Core开发日志——简述路由
原文地址:https://www.cnblogs.com/kenwoo/p/9484499.html