Asp.net core mapcontrollers 背后干了些啥

1.背景

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

Asp.net core mapcontrollers 背后干了些啥

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的信息,具体实现类应该是            //ActionDescriptorCollectionProvider            IActionDescriptorCollectionProvider actions,            ActionEndpointFactory factory)        {            _dataSourceIdProvider = dataSourceIdProvider;            _actions = actions;            _factory = factory;        }
        public ControllerActionEndpointDataSource Create(OrderedEndpointsSequenceProvider orderProvider)        {               //直接new一个 datasource            return new ControllerActionEndpointDataSource(_dataSourceIdProvider, _actions, _factory, orderProvider);        }    }

终于看到我们的datascource了,所以进去自己看看,如果构建的endpoint.以及最终实现的类是哪个。直接点进去类,可以看到类的构造函数(没贴出来),实际上当我们第一次启动应用的时候,endpoint是没有构建出来的,直到第一次访问,才会进行初始化. 可以看到调用链 Initialize() =>UpdateEndpoints()=>CreateEndpoints()

internal abstract class ActionEndpointDataSourceBase : EndpointDataSource, IDisposable{     //获取endpoints     public 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 token                var 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 token                oldCancellationTokenSource?.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 now            for (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到builder                AddActionDataToBuilder(                    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 here            if (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的时候更加得心应手了.