许多中间件都会为IApplicationBuilder添加一个扩展方法以简化调用,比如在2.x中常见的UseRoute()之类,其实就是简单调用UseMiddleware<Xxx>()。在ASP.NET Core的MVC也同样提供了一个UseMvc()扩展方法,但是和会转换为UseMiddleware<XMiddleware>()的那些方法不同,并没有一个与UseMvc()对应的、名称为MvcMiddleware的中间件存在。
其实,所谓的UseMvc()其实不过是对UseRouter()的封装:
// project:
// aspnet/Mvc
// file:
// src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcApplicationBuilderExtensions.cs
public static IApplicationBuilder UseMvc( this IApplicationBuilder app, Action<IRouteBuilder> configureRoutes)
{
if (app == null){ throw new ArgumentNullException(nameof(app)); }
if (configureRoutes == null){ throw new ArgumentNullException(nameof(configureRoutes)); }
// throw an exception if there's no MvcMarkerService
VerifyMvcIsRegistered(app);
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());
}
可以看到,执行app.UseMvc(rb=>{ /**/ }) 之类的操作,其实是在配置一个路由中间件,其默认处理器是MvcRouterHandler的实例。这个MvcRouterHandler继承自IRouter,核心思想便是先进行MVC匹配,如果匹配不成功,则返回Task.CompletedTask(根据路由中间件的实现原理,之后会交由后续中间件进行处理);否则,获取路由匹配到的数据,设置处理器为调用相关动作。源码如下:
public class MvcRouteHandler : IRouter{
private readonly IActionContextAccessor _actionContextAccessor;
private readonly IActionInvokerFactory _actionInvokerFactory;
private readonly IActionSelector _actionSelector;
private readonly ILogger _logger;
private readonly DiagnosticSource _diagnosticSource;
// ...
public Task RouteAsync(RouteContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(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;
}
// ...
}
总结来说,这里的代码主要完成以下工作:
-
这里一旦路由匹配,则可以得知当前路由所命中
Action,ASP.NET Core使用ActionDescriptor描述该Action。其中包含了当前Action方法名、参数描述符表、过滤器描述表、Action约束、RouteValue等信息。 -
作为一个
IRouter,路由匹配后自然还要设置Handler以示命中:这里也就是构建一个routeContext.Handler。该Handler执行以下基本处理过程:- 根据当前
HttpContext,配合当前路由数据、及所命中Action的ActionDescriptor,构建上下文ActionContext:
var actionContext = new ActionContext(httpContext, routeData, actionDescriptor)- 根据
ActionContext,创建IActionInvoker。 - 执行
IActionInvoker.InvokeAsync(),触发 MVCpipeline的调用
具体的细节,比如IActionInvoker是如何创建的,在后续相关笔记中记录。
- 根据当前
本文解析了ASP.NET Core中UseMvc方法的底层实现,指出它是对UseRouter的封装,着重介绍了MvcRouterHandler的工作原理,包括路由匹配、ActionDescriptor处理和MVC pipeline调用流程。
581

被折叠的 条评论
为什么被折叠?



