许多中间件都会为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
是如何创建的,在后续相关笔记中记录。
- 根据当前