asp.net core mvc剖析:路由

时间:2023-03-09 00:11:25
asp.net core mvc剖析:路由

在mvc框架中,任何一个动作请求都会被映射到具体控制器中的方法上,那框架是如何完成这样一个过程的,现在我们就来简单分析下流程。

我们紧跟上面的主题,任何一个请求都会交给处理管道进行处理,那mvc处理的流程自然也应该处于这个管道中,在startup.cs文件的Configure方法中,我们会看到这样的代码

app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}",
defaults: new { area = "admin" });
});

  这部分代码的作用我们都清楚,就是配置路由规则,把用户的请求,路由到控制器方法上,我们来看它里面怎么做到的。首先看下UseMvc方法,直接上代码:

public static IApplicationBuilder UseMvc(
this IApplicationBuilder app,
Action<IRouteBuilder> configureRoutes)
{
。。。。。。
//实例化路由构造器
var routes = new RouteBuilder(app)
{
//设置默认处理器,就是路由符合条件时使用MvcRouteHandler来处理请求
DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(),
};
//配置路由规则
configureRoutes(routes);
//这句很重要,上面配置的全局的路由规则,我们同样可以在控制器或者控制器方法上使用RouteAttribute配置路由规则,这些规则会优先采用
routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices));
//routes.Build方法生成IRouter对象,一会我们在看具体细节,然后通过UseRouter注册一个RouterMiddleware中间件
return app.UseRouter(routes.Build());
}

  

  我们来看下Build方法代码:

public IRouter Build()
{
//创建一个路由规则集合
var routeCollection = new RouteCollection();
//把配置的路由规则加入到集合中,这个Routes就是上面configureRoutes(routes)配置的
foreach (var route in Routes)
{
routeCollection.Add(route);
} return routeCollection;
}

  

  configureRoutes中,通过MapRoute方法注册规则,我们看一下:

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>();
//new了一个Route对象,把这个对象加入到routeBuilder.Routes集合中
routeBuilder.Routes.Add(new Route(
routeBuilder.DefaultHandler,
name,
template,
new RouteValueDictionary(defaults),
new RouteValueDictionary(constraints),
new RouteValueDictionary(dataTokens),
inlineConstraintResolver)); return routeBuilder;
}

  路由规则配置好了,当用户请求过来后,又是如何进行匹配的?上面我们提到了RouterMiddleware中间件,用户请求会通过这个中间件进行处理,这个中间件Invoke方法的代码:

public async Task Invoke(HttpContext httpContext)
{
var context = new RouteContext(httpContext);
//这句目前没有搞清楚作用是什么
context.RouteData.Routers.Add(_router);
//_router就是我们上面通过Build方法创建的,它就是RouteCollection
await _router.RouteAsync(context);
//判断是否找到了匹配的规则,这里的Handler只有当规则匹配了,才会赋值,Handler是什么?留一个疑问
if (context.Handler == null)
{
_logger.RequestDidNotMatchRoutes();
//如果没有任何路由符合要求,直接执行下一个中间件
await _next.Invoke(httpContext);
}
else
{
httpContext.Features[typeof(IRoutingFeature)] = new RoutingFeature()
{
RouteData = context.RouteData,
};
//使用Handler处理请求
await context.Handler(context.HttpContext);
}
}

  

  下面来看RouteCollection的RouteAsync方法实现:

public async virtual Task RouteAsync(RouteContext context)
{
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
{
//调用Route对象的RouteAsync方法,匹配规则
await route.RouteAsync(context);
//规则匹配成功,直接break
if (context.Handler != null)
{
break;
}
}
finally
{
if (context.Handler == null)
{
snapshot.Restore();
}
}
}
}

  

  具体匹配方式不再介绍了,就是根据请求的路径跟设置的地址规则进行对比,我们只看匹配成功后,做了什么?

protected override Task OnRouteMatched(RouteContext context)
{
context.RouteData.Routers.Add(_target);
       //_target是routeBuilder.DefaultHandler,这个是在上面创建routeBuilder时设置的,是一个MvcRouteHandler
       return _target.RouteAsync(context);
}

  

  在MvcRouteHandler里完成了context.Handler的设置,下面是这个Handler的具体代码:

context.Handler = (c) =>
{
var routeData = c.GetRouteData(); var actionContext = new ActionContext(context.HttpContext, routeData, actionDescriptor);
if (_actionContextAccessor != null)
{
_actionContextAccessor.ActionContext = actionContext;
}
//根据请求的动作信息创建一个IActionInvoker对象
var invoker = _actionInvokerFactory.CreateInvoker(actionContext);
if (invoker == null)
{
throw new InvalidOperationException(
Resources.FormatActionInvokerFactory_CouldNotCreateInvoker(
actionDescriptor.DisplayName));
}
//完成动作执行
return invoker.InvokeAsync();
};

  上面调用过程如下图:

asp.net core mvc剖析:路由

  后面再详细介绍mvc具体执行过程。