Asp.net core mapcontrollers 背后干了些啥

Posted guoxiaotian

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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.
            // 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

以上是关于Asp.net core mapcontrollers 背后干了些啥的主要内容,如果未能解决你的问题,请参考以下文章

Asp.NET Core进阶 第四篇 Asp.Net Core Blazor框架

.NET Core 1.0ASP.NET Core 1.0和EF Core 1.0简介

asp.net core 注入后仍然报错?

深入研究 Mini ASP.NET Core(迷你 ASP.NET Core),看看 ASP.NET Core 内部到底是如何运行的

.Net Core 学习 - ASP.NET Core 概念学习

ASP.NET Core MVC 2.x 全面教程_ASP.NET Core MVC 14. ASP.NET Core Identity 入门