Asp.net core mapcontrollers 背后干了些啥

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的信息,具体实现类应该是
            //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. 为每一个action添加相关终结点
            // 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的时候更加得心应手了.

有兴趣可以看看以下文章

ASP.NET Core Controller与IOC的羁绊
.NET Core开发日志——Controller

posted @ 2023-03-05 22:07  果小天  阅读(936)  评论(2编辑  收藏  举报