1.背景
当我们在写webapi的时候我们发现,框架自动帮我们写好了 app.MapControllers(),看注释写的是帮我们将controllerl里面的action映射为我们的终结点,那具体是怎么弄得呢,我觉得可以仔细研究一下,看一下背后的逻辑..

2.开始研究,用dnspy看一下源码
public static ControllerActionEndpointConventionBuilder MapControllers(this IEndpointRouteBuilder endpoints){//确保依赖的服务已经注册 可以进去看看帮我们注入了哪些东西,后面的代码可以看到它们身影ControllerEndpointRouteBuilderExtensions.EnsureControllerServices(endpoints);return ControllerEndpointRouteBuilderExtensions.GetOrCreateDataSource(endpoints).DefaultBuilder;}//接上面,创建endpoint数据源private static ControllerActionEndpointDataSource GetOrCreateDataSource(IEndpointRouteBuilder endpoints){ControllerActionEndpointDataSource controllerActionEndpointDataSource = endpoints.DataSources.OfType<ControllerActionEndpointDataSource>().FirstOrDefault<ControllerActionEndpointDataSource>();if (controllerActionEndpointDataSource == null){//相关服务OrderedEndpointsSequenceProviderCache requiredService = endpoints.ServiceProvider.GetRequiredService<OrderedEndpointsSequenceProviderCache>();//获取数据源controllerActionEndpointDataSource = endpoints.ServiceProvider.GetRequiredService<ControllerActionEndpointDataSourceFactory>().Create(requiredService.GetOrCreateOrderedEndpointsSequenceProvider(endpoints));endpoints.DataSources.Add(controllerActionEndpointDataSource);}return controllerActionEndpointDataSource;}
重点就是 ControllerActionEndpointDataSourceFactory.Create() 函数帮我们构建数据源。
controllerActionEndpointDataSourceFactory
internal class ControllerActionEndpointDataSourceFactory{private readonly ControllerActionEndpointDataSourceIdProvider _dataSourceIdProvider;private readonly IActionDescriptorCollectionProvider _actions;private readonly ActionEndpointFactory _factory;public ControllerActionEndpointDataSourceFactory(ControllerActionEndpointDataSourceIdProvider dataSourceIdProvider,//这个很重要,为我们提供controller action的信息,具体实现类应该是//ActionDescriptorCollectionProviderIActionDescriptorCollectionProvider actions,ActionEndpointFactory factory){_dataSourceIdProvider = dataSourceIdProvider;_actions = actions;_factory = factory;}public ControllerActionEndpointDataSource Create(OrderedEndpointsSequenceProvider orderProvider){//直接new一个 datasourcereturn new ControllerActionEndpointDataSource(_dataSourceIdProvider, _actions, _factory, orderProvider);}}
终于看到我们的datascource了,所以进去自己看看,如果构建的endpoint.以及最终实现的类是哪个。直接点进去类,可以看到类的构造函数(没贴出来),实际上当我们第一次启动应用的时候,endpoint是没有构建出来的,直到第一次访问,才会进行初始化. 可以看到调用链 Initialize() =>UpdateEndpoints()=>CreateEndpoints()
internal abstract class ActionEndpointDataSourceBase : EndpointDataSource, IDisposable{//获取endpointspublic override IReadOnlyList<Endpoint> Endpoints{get{Initialize();return _endpoints;}}private void Initialize(){if (_endpoints == null){lock (Lock){if (_endpoints == null){UpdateEndpoints();}}}}private void UpdateEndpoints(){lock (Lock){var endpoints = CreateEndpoints(_actions.ActionDescriptors.Items, Conventions);// See comments in DefaultActionDescriptorCollectionProvider. These steps are done// in a specific order to ensure callers always see a consistent state.// Step 1 - capture old tokenvar oldCancellationTokenSource = _cancellationTokenSource;// Step 2 - update endpoints_endpoints = endpoints;// Step 3 - create new change token_cancellationTokenSource = new CancellationTokenSource();_changeToken = new CancellationChangeToken(_cancellationTokenSource.Token);// Step 4 - trigger old tokenoldCancellationTokenSource?.Cancel();}}}internal class ControllerActionEndpointDataSource:ActionEndpointDataSourceBase{protected override List<Endpoint> CreateEndpoints(IReadOnlyList<ActionDescriptor> actions, IReadOnlyList<Action<EndpointBuilder>> conventions){var endpoints = new List<Endpoint>();var keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);// MVC guarantees that when two of it's endpoints have the same route name they are //equivalent.// However, Endpoint Routing requires Endpoint Names to be unique.var routeNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);// For each controller action - add the relevant endpoints.// 1. If the action is attribute routed, we use that information verbatim// 2. If the action is conventional routed// a. Create a *matching only* endpoint for each action X route (if possible)// b. Ignore link generation for nowfor (var i = 0; i < actions.Count; i++){if (actions[i] is ControllerActionDescriptor action){//逻辑重点,为每个action构建一个endpoint._endpointFactory.AddEndpoints(endpoints, routeNames, action, _routes, conventions, CreateInertEndpoints);if (_routes.Count > 0){// If we have conventional routes, keep track of the keys so we can create// the link generation routes later.foreach (var kvp in action.RouteValues){keys.Add(kvp.Key);}}}}// Now create a *link generation only* endpoint for each route. This gives us a very// compatible experience to previous versions.for (var i = 0; i < _routes.Count; i++){var route = _routes[i];_endpointFactory.AddConventionalLinkGenerationRoute(endpoints, routeNames, keys, route, conventions);}return endpoints;}}
可以看到我们针对于每个action构建一个endpoint,AddEndpoints()所以继续进去看看逻辑。
public void AddEndpoints( List<Endpoint> endpoints, HashSet<string> routeNames,ActionDescriptor action,IReadOnlyList<ConventionalRouteEntry> routes,IReadOnlyList<Action<EndpointBuilder>> conventions, bool createInertEndpoints){//不走的逻辑 代码被我省略删除 路由模板不为空。if (action.AttributeRouteInfo?.Template != null){//构建出来处理去请求的委托.很重要var requestDelegate = CreateRequestDelegate(action) ?? _requestDelegate;//属性路由模板var attributeRoutePattern = RoutePatternFactory.Parse(action.AttributeRouteInfo.Template);// Modify the route and required values to ensure required values can be successfully subsituted.// Subsitituting required values into an attribute route pattern should always succeed.var (resolvedRoutePattern, resolvedRouteValues) = ResolveDefaultsAndRequiredValues(action, attributeRoutePattern);var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(resolvedRoutePattern, resolvedRouteValues);//省略不重要代码var builder = new RouteEndpointBuilder(requestDelegate, updatedRoutePattern, action.AttributeRouteInfo.Order){DisplayName = action.DisplayName,};//添加action data到builderAddActionDataToBuilder(builder,routeNames,action,action.AttributeRouteInfo.Name,dataTokens: null,action.AttributeRouteInfo.SuppressLinkGeneration,action.AttributeRouteInfo.SuppressPathMatching,conventions,perRouteConventions: Array.Empty<Action<EndpointBuilder>>());endpoints.Add(builder.Build());}}
CreateRequestDelegate可以看到构建处理器的逻辑,我们进去看看,由于是ioc注入的容器所以我们需要找到实现类。实现类是 ControllerRequestDelegateFactory类
ControllerRequestDelegateFactory
private RequestDelegate? CreateRequestDelegate(ActionDescriptor action, RouteValueDictionary? dataTokens = null){foreach (var factory in _requestDelegateFactories){ //看来我们可以注入我们的处理器逻辑,需要注明一下顺序(或者是我记得后来注入的在最前面?)var requestDelegate = factory.CreateRequestDelegate(action, dataTokens);if (requestDelegate != null){return requestDelegate;}}return null;}// ControllerRequestDelegateFactory方法public RequestDelegate? CreateRequestDelegate(ActionDescriptor actionDescriptor, RouteValueDictionary? dataTokens){ //省略不重要代码//针对于 context的委托.非常完美return context =>{RouteData routeData;if (dataTokens is null or { Count: 0 }){routeData = new RouteData(context.Request.RouteValues);}else{routeData = new RouteData();routeData.PushState(router: null, context.Request.RouteValues, dataTokens);}var actionContext = new ActionContext(context, routeData, actionDescriptor);var controllerContext = new ControllerContext(actionContext){// PERF: These are rarely going to be changed, so let's go copy-on-write.ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory> (_valueProviderFactories)};controllerContext.ModelState.MaxAllowedErrors = _maxModelValidationErrors;//实际上这点很重要,这里是帮助我们构建controller类的,因为控制器类没有注入ioc容器var (cacheEntry, filters) = _controllerActionInvokerCache.GetCachedResult(controllerContext);//很重要的一个东西,直接把我们引入到mvc的世界中,各种filter逻辑都在这var invoker = new ControllerActionInvoker(_logger,_diagnosticListener,_actionContextAccessor,_mapper,controllerContext,cacheEntry,filters);return invoker.InvokeAsync();};}
看了半天找到了构建controller类的逻辑,那我们进去看看,既然没有注入到ioc,那么是如何构建的呢.进去getcacheResult中看看。
Controller类的生成逻辑
public (ControllerActionInvokerCacheEntry cacheEntry, IFilterMetadata[] filters) GetCachedResult(ControllerContext controllerContext){var actionDescriptor = controllerContext.ActionDescriptor;// We don't care about thread safety hereif (cacheEntry is null){//省略 部分代码//这个是重点,实际上是一个委托,为我们生成controller,下面有代码var controllerFactory =_controllerFactoryProvider.CreateControllerFactory(actionDescriptor);var controllerReleaser = _controllerFactoryProvider.CreateAsyncControllerReleaser(actionDescriptor);// 省略 部分代码cacheEntry = new ControllerActionInvokerCacheEntry(filterFactoryResult.CacheableFilters,controllerFactory,controllerReleaser,propertyBinderFactory,objectMethodExecutor,actionMethodExecutor);actionDescriptor.CacheEntry = cacheEntry;}// 省略 ......return (cacheEntry, filters);}
逻辑是一个provider构建委托生成的,注入的实现类是 ControllerFactoryProvider 看看逻辑
ControllerFactoryProvider
public Func<ControllerContext, object> CreateControllerFactory(ControllerActionDescriptor descriptor){//省略 代码var controllerType = descriptor.ControllerTypeInfo?.AsType();if (_factoryCreateController != null){ //第一种逻辑return _factoryCreateController;}var controllerActivator = _activatorProvider.CreateActivator(descriptor);var propertyActivators = GetPropertiesToActivate(descriptor);//第二种逻辑object CreateController(ControllerContext controllerContext){var controller = controllerActivator(controllerContext);for (var i = 0; i < propertyActivators.Length; i++){var propertyActivator = propertyActivators[i];propertyActivator(controllerContext, controller);}return controller;}return CreateController;}
返回生成controller类的委托有两种逻辑 ,实际上我们走的是第一种 即 _factoryCreateController ,它在构造的时候被初始化为 _factoryCreateController = controllerFactory.CreateController;controllerFactory类又是哪个类呢,实际上是我们注入的DefaultControllerFactory 类,很熟悉把,因为以前的版本也是这么做的
DefaultControllerFactory
public object CreateController(ControllerContext context){//重点代码var controller = _controllerActivator.Create(context);foreach (var propertyActivator in _propertyActivators){propertyActivator.Activate(context, controller);}return controller;}
根据你的一个controllercontext生成我们的控制类,_controllerActivator的实现类实际上我们注入的是 DefaultControllerActivator,进去具体看看
DefaultControllerActivator
public object Create(ControllerContext controllerContext){//核心代码 删除了一些代码var controllerTypeInfo = controllerContext.ActionDescriptor.ControllerTypeInfo;var serviceProvider = controllerContext.HttpContext.RequestServices;return _typeActivatorCache.CreateInstance<object>(serviceProvider, controllerTypeInfo.AsType());}
引出了新的类 _typeActivatorCache ,这个类的注入实际上是 TypeActivatorCache
TypeActivatorCache
internal class TypeActivatorCache : ITypeActivatorCache{private readonly Func<Type, ObjectFactory> _createFactory =(type) => ActivatorUtilities.CreateFactory(type, Type.EmptyTypes);private readonly ConcurrentDictionary<Type, ObjectFactory> _typeActivatorCache =new ConcurrentDictionary<Type, ObjectFactory>();/// <inheritdoc/>public TInstance CreateInstance<TInstance>(IServiceProvider serviceProvider,Type implementationType){//核心代码 其他代码删除var createFactory = _typeActivatorCache.GetOrAdd(implementationType, _createFactory);return (TInstance)createFactory(serviceProvider, arguments: null);}}
那么生成controller的大佬终于出来了 ActivatorUtilities 是这个类 ,名字听着就霸气,进一步进去 结果看不到了,因为asp.net core的源码没有这部分。所以就在这停下吧 .如果你想更深入的了解不妨看看这篇文章,深入且详细 ASP.NET Core Controller与IOC的羁绊
controller类的生成总结
它不是托管到ioc的,而是通过其他的方式获取 ,获取逻辑:ControllerActionInvokerCache.GetCachedResult()=>ControllerFactoryProvider.CreateControllerFactory()=>DefaultControllerFactory.CreateController()=>DefaultControllerActivator.Create()=>TypeActivatorCache.CreateInstance()=>ActivatorUtilities.CreateFactory(type, Type.EmptyTypes);这个就是一步步的调用逻辑,不像我写逻辑,直接new一个完事
controller类的调用逻辑
上面知道了 controller源头从哪来,那现在就要寻找他的出生地。继续找逻辑.我们知道CreateRequestDelegate生成处理委托,这就是处理整个处理逻辑,返回了一个ControllerActionInvoker 。
var invoker = new ControllerActionInvoker(_logger,_diagnosticListener,_actionContextAccessor,_mapper,controllerContext,cacheEntry,filters);return invoker.InvokeAsync();
实际上是调用 InvokeAsync().这个就吊了,逻辑就涉及到我们的各种过滤器了.
ResourceInvoker
public virtual Task InvokeAsync(){_actionContextAccessor.ActionContext = _actionContext;var scope = _logger.ActionScope(_actionContext.ActionDescriptor);Task task;try{ // 调用filter管道task = InvokeFilterPipelineAsync();}catch (Exception exception){return Awaited(this, Task.FromException(exception), scope);}if (!task.IsCompletedSuccessfully){return Awaited(this, task, scope);}static async Task Awaited(ResourceInvoker invoker, Task task, IDisposable? scope){try{await task;}finally{await invoker.ReleaseResourcesCore(scope);}}}
InvokeFilterPipelineAsync
private Task InvokeFilterPipelineAsync(){var next = State.InvokeBegin;// The `scope` tells the `Next` method who the caller is, and what kind of state to initialize to// communicate a result. The outermost scope is `Scope.Invoker` and doesn't require any type// of context or result other than throwing.var scope = Scope.Invoker;// The `state` is used for internal state handling during transitions between states. In practice this// means storing a filter instance in `state` and then retrieving it in the next state.var state = (object?)null;// `isCompleted` will be set to true when we've reached a terminal state.var isCompleted = false;try{while (!isCompleted){var lastTask = Next(ref next, ref scope, ref state, ref isCompleted);if (!lastTask.IsCompletedSuccessfully){return Awaited(this, lastTask, next, scope, state, isCompleted);}}return Task.CompletedTask;}catch (Exception ex){// Wrap non task-wrapped exceptions in a Task,// as this isn't done automatically since the method is not async.return Task.FromException(ex);}static async Task Awaited(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object? state, bool isCompleted){await lastTask;while (!isCompleted){await invoker.Next(ref next, ref scope, ref state, ref isCompleted);}}}
结合 next函数实用 (贴部分代码)
next函数
private Task Next(ref State next, ref Scope scope, ref object? state, ref bool isCompleted){switch (next){case State.InvokeBegin:{goto case State.AuthorizationBegin;}case State.AuthorizationBegin:{_cursor.Reset();goto case State.AuthorizationNext;}//删除了一堆代码case State.ActionBegin:{//实际上调用此代码var task = InvokeInnerFilterAsync();if (!task.IsCompletedSuccessfully){next = State.ActionEnd;return task;}goto case State.ActionEnd;}case State.ActionEnd:{if (scope == Scope.Exception){// If we're inside an exception filter, let's allow those filters to 'unwind' before// the result.isCompleted = true;return Task.CompletedTask;}var task = InvokeResultFilters();if (!task.IsCompletedSuccessfully){next = State.ResourceInsideEnd;return task;}goto case State.ResourceInsideEnd;}case State.InvokeEnd:{isCompleted = true;return Task.CompletedTask;}default:throw new InvalidOperationException();}}
可以看到上面的调用逻辑,实际上是状态机,指明下一步调用的函数和逻辑,然后直到完全完成任务,我们可以看到 当actionbegin开始的时候调用的任务是 InvokeInnerFilterAsync,可以点进去具体看看,具体类是ControllerActionInvoker
ControllerActionInvoker
protected override Task InvokeInnerFilterAsync(){try{var next = State.ActionBegin;var scope = Scope.Invoker;var state = (object?)null;var isCompleted = false;while (!isCompleted){//重点代码var lastTask = Next(ref next, ref scope, ref state, ref isCompleted);if (!lastTask.IsCompletedSuccessfully){//返回task ,实际保证了内部逻辑执行的有序性。return Awaited(this, lastTask, next, scope, state, isCompleted);}}return Task.CompletedTask;}static async Task Awaited(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, object? state, bool isCompleted){ //参数都是枚举,值类型 或者结构 这点很重要.await lastTask;while (!isCompleted){await invoker.Next(ref next, ref scope, ref state, ref isCompleted);}}}private Task Next(ref State next, ref Scope scope, ref object? state, ref bool isCompleted){switch (next){case State.ActionBegin:{var controllerContext = _controllerContext;//游标指针归0,指向当前的filter位置._cursor.Reset();//调用了 委托生成 controller实例. 生成controller_instance = _cacheEntry.ControllerFactory(controllerContext);var task = BindArgumentsAsync();if (task.Status != TaskStatus.RanToCompletion){next = State.ActionNext;return task;}goto case State.ActionNext;}// 如果filter调用完了,那么就进去到这个方法.case State.ActionInside:{//调用controller的action方法.var task = InvokeActionMethodAsync();if (task.Status != TaskStatus.RanToCompletion){next = State.ActionEnd;return task;}goto case State.ActionEnd;}}// 调用的具体方法private Task InvokeActionMethodAsync(){// 保留了一些重要代码.var objectMethodExecutor = _cacheEntry.ObjectMethodExecutor;var actionMethodExecutor = _cacheEntry.ActionMethodExecutor;var orderedArguments = PrepareArguments(_arguments, objectMethodExecutor);//调用 controller action方法.var actionResultValueTask = actionMethodExecutor.Execute(_mapper, objectMethodExecutor, _instance!, orderedArguments);if (actionResultValueTask.IsCompletedSuccessfully){_result = actionResultValueTask.Result;}else{return Awaited(this, actionResultValueTask);}return Task.CompletedTask;}
此时我们终于找到了 controller的调用逻辑链,同时我们也找到了 我们注册的过滤器在哪个位置被调用,它为我们揭开了神秘的面纱。invoker.InvokeAsync()=>InvokeFilterPipelineAsync()=>next() (状态机调用了我们注入的filter)=>InvokeActionMethodAsync()。
总结
做个总结,首先来说目标是想看看mapcontroller背后做了哪些事情,可以看到,它实际上给我们注册了endpoint数据源,通过endpointdatasourcefactory创建一个datasource(new 一个),实际上是我们为每一个action创建一个RouteEndpoint, 通过CreateRequestDelegate方法创建处理请求的委托,然后通过调用DefaultControllerFactory类为我们创建controller实例,并没有从ioc中获取,然后我们在我们在处理请求的时候,通过引入状态机的调用模式,将我们注入的filters来对请求进行先一步的处理,同时调用InvokeActionMethodAsync方法完成请求的处理。大概的逻辑就是这样。还有我们注入了endpointdatasource,所以才有userouting,useendpoint等进而注入一些重要的中间件,有兴趣的可以看看上一篇文章。
总结的总结,对着源码找了一下调用的逻辑,以后写bug的时候更加得心应手了.