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的时候更加得心应手了.
有兴趣可以看看以下文章