asp net core
😋 asp net core
2022/6/5 10:25:37
➡️

asp.net core

controller

controller 默认不是有 DI 注入的,而是在路由中间件里,通过 通过 ActivatorUtilities (Lambda 表达式树)实例化的

创建控制器的过程依赖众多不同的提供者和工厂类,但最终是由实现 IControllerActivator 接口的实 例来决定的。实现类只需要实现两个方法

控制器的实例是在资源管理器的 OnResourceExecuting 执行后生成的,因为资源管理器有可能会短路 管道,如果短路了管道就没有在生成控制器对象的意义了

public interface IControllerActivator
{
    object Create(ControllerContext context);
    void Release(ControllerContext context, object controller);
}

如上的接口由俩个实现类

ServiceBasedControllerActivator 是通过依赖注入解析 controller

using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.AspNetCore.Mvc.Controllers;

public class ServiceBasedControllerActivator : IControllerActivator
{

    public object Create(ControllerContext actionContext)
    {
        if (actionContext == null)
        {
            throw new ArgumentNullException(nameof(actionContext));
        }

        var controllerType = actionContext.ActionDescriptor.ControllerTypeInfo.AsType();

        return actionContext.HttpContext.RequestServices.GetRequiredService(controllerType);
    }

    public virtual void Release(ControllerContext context, object controller)
    {
    }
}

DefaultControllerActivator 是默认的生成器,通过表达式树实例化,如果由多个请求进来,实例化 的控制器放在一个并发的集合里

using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.AspNetCore.Mvc.Infrastructure;

namespace Microsoft.AspNetCore.Mvc.Controllers;

internal sealed class DefaultControllerActivator : IControllerActivator
{
    private readonly ITypeActivatorCache _typeActivatorCache;


    public DefaultControllerActivator(ITypeActivatorCache typeActivatorCache)
    {
        if (typeActivatorCache == null)
        {
            throw new ArgumentNullException(nameof(typeActivatorCache));
        }

        _typeActivatorCache = typeActivatorCache;
    }

    public object Create(ControllerContext controllerContext)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException(nameof(controllerContext));
        }

        if (controllerContext.ActionDescriptor == null)
        {
            throw new ArgumentException(Resources.FormatPropertyOfTypeCannotBeNull(
                nameof(ControllerContext.ActionDescriptor),
                nameof(ControllerContext)));
        }

        var controllerTypeInfo = controllerContext.ActionDescriptor.ControllerTypeInfo;

        if (controllerTypeInfo == null)
        {
            throw new ArgumentException(Resources.FormatPropertyOfTypeCannotBeNull(
                nameof(controllerContext.ActionDescriptor.ControllerTypeInfo),
                nameof(ControllerContext.ActionDescriptor)));
        }

        var serviceProvider = controllerContext.HttpContext.RequestServices;
        return _typeActivatorCache.CreateInstance<object>(serviceProvider,
        controllerTypeInfo.AsType());
    }

    public void Release(ControllerContext context, object controller)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (controller == null)
        {
            throw new ArgumentNullException(nameof(controller));
        }

        if (controller is IDisposable disposable)
        {
            disposable.Dispose();
        }
    }

    public ValueTask ReleaseAsync(ControllerContext context, object controller)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (controller == null)
        {
            throw new ArgumentNullException(nameof(controller));
        }

        if (controller is IAsyncDisposable asyncDisposable)
        {
            return asyncDisposable.DisposeAsync();
        }

        Release(context, controller);
        return default;
    }
}

过滤器

过滤器

ActionFilter、ResultFilter、ResourceFilter,IAsyncAlwaysRunResultFilter 中只要是对 Context.Result 赋值,就不再继续往后了。

IAsyncAuthorizationFilter

一般不使用

IAsyncResourceFilter 资源管理器

更多的用处是缓存,可以在过滤器的开头直接短路过滤器管道,直接返回缓存的结果 IAsyncResourceFilter 无论异常是否发生都会执行,而且不能处理异常在此过滤器里

IAsyncResourceFilter 执行后,先创建控制器实例,之后进行模型绑定

public class MyResourceFilter:IAsyncResourceFilter {
  public async Task OnResourceExecutionAsync(ResourceExecutingContext context,
  ResourceExecutionDelegate next) {
    Console.WriteLine("ResourceFilter before");
    /*
    直接返回结果,短路管道
    context.Result = new ObjectResult("ok-MyResourceFilter");
     */

    //执行余下的管道
    await next();
    /*
    设置返回结果没有效果,因为 asp.net core 任务后面的管道一定会设置响应值
    context.Result = new ObjectResult("ok-MyResourceFilter");
     */
    Console.WriteLine("ResourceFilter after");
  }
}

IAsyncExceptionFilter 异常过滤器

异常过滤器处理 model bind ,controller 及 action 中发生的异常,唯一能处理异常的过滤器,其他 过滤器里使用 try catch 处理异常没有用,异常过滤器只有在异常发生的时候才会被调用

public class MyExceptionFilter: IAsyncExceptionFilter {
  public  Task OnExceptionAsync(ExceptionContext context) {
     Console.WriteLine("ExceptionFilter process ");

     //若要处理异常,请将 ExceptionHandled 属性设置为 true,或分配 Result 属性。 这将停止传播异常。
     //异常代表处理过了,类似 try catch
     context.ExceptionHandled = true;
     //context.Result = new ObjectResult("ok");
     return  Task.CompletedTask;
  }
}

IAsyncActionFilter

public class MyActionFilter : IAsyncActionFilter{
  public async Task OnActionExecutionAsync(
    ActionExecutingContext context, ActionExecutionDelegate next) {
    try {
      Console.WriteLine("MyActionFilter before");
      /**
       这里可以直接设置  Result 并返回,但是不要这样做,这样就失去了  ActionFilter  的意义
       context.Result = new ObjectResult("ok");
       */

      await next();
      Console.WriteLine("MyActionFilter after");
    }
    catch (Exception e) {
      Console.WriteLine(e);
    }
  }
}

IResultFilter

IResultFilter 是在 action 正常执行,没有异常抛出才会执行,可以修改返回的结果

public class MyResultFilter: IResultFilter {
  public void OnResultExecuting(ResultExecutingContext context) {
    //可以修改 action 的返回结果
    context.Result = new ObjectResult("MyResultFilter result");
    /*
      直接返回,没有任何结果,短路后续操作
      context.Cancel = true;
    */
    Console.WriteLine("MyResultFilter before");MyResultFilter result
  }

  public void OnResultExecuted(ResultExecutedContext context) {
    Console.WriteLine("MyResultFilter after");
  }
}

ActionFilterAttribute

由于操作过滤器常常在应用中的使用比较频繁,所以这里详细介绍一下它的使用。ASP.NET Core 框架 提供了一个抽象类 ActionFilterAttribute,该抽象类实现了多个接口,还继承了 Attribute,允许我 们以特性的方式使用。所以,一般比较建议大家通过继承该抽象类来自定义操作过滤器

public abstract class ActionFilterAttribute :
    Attribute,
    IActionFilter,
    IFilterMetadata,
    IAsyncActionFilter,
    IResultFilter,
    IAsyncResultFilter,
    IOrderedFilter
  {

    public int Order { get; set; }

    public virtual void OnActionExecuting(ActionExecutingContext context)
    {
    }

    public virtual void OnActionExecuted(ActionExecutedContext context)
    {
    }

    public virtual async Task OnActionExecutionAsync(
      ActionExecutingContext context,
      ActionExecutionDelegate next)
    {
      if (context == null)
        throw new ArgumentNullException(nameof (context));
      if (next == null)
        throw new ArgumentNullException(nameof (next));
      this.OnActionExecuting(context);
      if (context.Result != null)
        return;
      this.OnActionExecuted(await next());
    }

    public virtual void OnResultExecuting(ResultExecutingContext context)
    {
    }

    public virtual void OnResultExecuted(ResultExecutedContext context)
    {
    }

    public virtual async Task OnResultExecutionAsync(
      ResultExecutingContext context,
      ResultExecutionDelegate next)
    {
      if (context == null)
        throw new ArgumentNullException(nameof (context));
      if (next == null)
        throw new ArgumentNullException(nameof (next));
      this.OnResultExecuting(context);
      if (context.Cancel)
        return;
      this.OnResultExecuted(await next());
    }
  }
}

IAsyncAlwaysRunResultFilter 无论是否有异常都会执行

正常在 IResultFilter 之 后 执行

public interface IAsyncAlwaysRunResultFilter : IAsyncResultFilter, IFilterMetadata
{
}
public class MyAlwaysRunResultFilter: IAsyncAlwaysRunResultFilter  {
  public async Task OnResultExecutionAsync(ResultExecutingContext context,
  ResultExecutionDelegate next) {
    //可以设置结果
    context.Result = new ObjectResult("MyAlwaysRunResultFilter  result");
    Console.WriteLine("MyAlwaysRunResultFilter before");
    await next();
    Console.WriteLine("MyAlwaysRunResultFilter after");
  }
}

过滤器的使用方式

全局使用拦截所有的 action

把过滤器实现 加入 FilterCollection 集合 Collection

builder.Services.AddControllers(opt => {
  // 内部 ActivatorUtilities.CreateInstance 生成实例,每次请求都会生成一个实例
  // 过滤器构造函数的参数有 DI 容器提供
  //默认同类过滤器执行顺序是注册顺序
  //可以给方法赋值  order
  opt.Filters.Add<MyResourceFilter>();
  opt.Filters.Add<MyExceptionFilter>();
  opt.Filters.Add<MyActionFilter>();
  opt.Filters.Add<MyResultFilter>();
  //这种是用户提供唯一的实例
  opt.Filters.Add(new MyExceptionFilter());
});

注入的过滤器有 DI 容器提供,生命周期有 DI 容器决定

builder.Services.AddScoped<MyExceptionFilter>();
builder.Services.AddControllers(opt => {
  //注册的全局过滤器来自 DI 容器
  opt.Filters.AddService<MyExceptionFilter>();
});

局部使用,构造函数参数不能使用依赖注入

这种过滤器不注入 DI 容器,所以构造函数参数要用户自己提供

public class MyActionFilter1 : Attribute, IAsyncActionFilter {
  public MyActionFilter1() {

  }
  public async Task OnActionExecutionAsync(
    ActionExecutingContext context, ActionExecutionDelegate next) {
    try {
      Console.WriteLine("MyActionFilter1 before");
      await next();
      Console.WriteLine("MyActionFilter1 after");
    }
    catch (Exception e) {
      Console.WriteLine(e);
    }
  }
}
[HttpGet("index")]
//在需要过滤器的 action 使用过滤器 attribute 标记,这样就不能使用依赖注入给拦截器的构造函数传值
//MyActionFilter1 也可以标记在 controller 上
[MyActionFilter1()]
public async Task<string> Get([FromServices]  IServiceScopeFactory factory ) {
  Console.WriteLine("action--------------------");
  await Task.CompletedTask;
  //throw new Exception("error");
  return " action result";
}

ServiceFilterAttribute 局部使用,DI 容器提供构造参数依赖

过滤器需要注入容器

namespace Microsoft.AspNetCore.Mvc
{
  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
  AllowMultiple = true, Inherited = true)]
  [DebuggerDisplay("ServiceFilter: Type={ServiceType} Order={Order}")]
  public class ServiceFilterAttribute : Attribute, IFilterFactory, IFilterMetadata, IOrderedFilter
  {
    public ServiceFilterAttribute(Type type)
    {
      this.ServiceType = type ?? throw new ArgumentNullException(nameof (type));
    }

    public int Order { get; set; }

    public Type ServiceType { get; }

    //如果设置为 true ,无论在 di 容器里 过滤器是什么生命周期,获取到的都是单例
    public bool IsReusable { get; set; }

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
      //通过服务定位器模式提供过滤器
      IFilterMetadata instance = serviceProvider != null ? (IFilterMetadata)
      serviceProvider.GetRequiredService(this.ServiceType) :
      throw new ArgumentNullException(nameof (serviceProvider));
      if (instance is IFilterFactory filterFactory)
        instance = filterFactory.CreateInstance(serviceProvider);
      return instance;
    }
  }
}
//过滤器需要注入容器
builder.Services.AddScoped<MyActionFilter1>();

[HttpGet("index")]
[ServiceFilter(typeof(MyActionFilter1))]
public async Task<string> Get([FromServices]  IServiceScopeFactory factory ) {
    Console.WriteLine("action--------------------");
    await Task.CompletedTask;
    return " action result";
}

TypeFilterAttribute 局部使用,DI 容器提供构造参数依赖

过滤器不要注入容器,默认过滤器是瞬时生成的

namespace Microsoft.AspNetCore.Mvc
{
  [AttributeUsage(AttributeTargets.Class |
  AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
  [DebuggerDisplay("TypeFilter: Type={ImplementationType} Order={Order}")]
  public class TypeFilterAttribute :
  Attribute, IFilterFactory, IFilterMetadata, IOrderedFilter
  {
    #nullable disable
    private ObjectFactory _factory;

    public TypeFilterAttribute(Type type)
    {
      this.ImplementationType = type ?? throw new ArgumentNullException(nameof (type));
    }
    public object[]? Arguments { get; set; }

    public Type ImplementationType { get; }

    public int Order { get; set; }
    //如果设置为 true ,就变成单例了
    public bool IsReusable { get; set; }

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
      if (serviceProvider == null)
        throw new ArgumentNullException(nameof (serviceProvider));

      if (this._factory == null)
      {
        object[] arguments = this.Arguments;
        Type[] typeArray;
        if (arguments == null)
        {
          typeArray = (Type[]) null;
        }
        else
        {
          IEnumerable<Type> source = ((IEnumerable<object>) arguments).
          Select<object, Type>((Func<object, Type>) (a => a.GetType()));
          typeArray = source != null ? source.ToArray<Type>() : (Type[]) null;
        }
        // ActivatorUtilities.CreateFactory 生成过滤器实例,
        //ActivatorUtilities.CreateFactory 内部会使用 DI 给过滤器的构造函数生成参数
        this._factory = ActivatorUtilities.CreateFactory(
          this.ImplementationType, typeArray ?? Type.EmptyTypes);
      }
      IFilterMetadata instance = (IFilterMetadata) this._factory(
        serviceProvider, this.Arguments);
      if (instance is IFilterFactory filterFactory)
        instance = filterFactory.CreateInstance(serviceProvider);
      return instance;
    }
  }
}
[HttpGet("index")]
[TypeFilter(typeof(MyActionFilter1))]
public async Task<string> Get([FromServices]  IServiceScopeFactory factory ) {
    Console.WriteLine("action--------------------");
    await Task.CompletedTask;
    return " action result";
}

过滤器执行顺序

先执行全局的,在执行局部的, 同类型的过滤器优先级通过,IOrderedFilter 进行调整

public interface IOrderedFilter : IFilterMetadata
{
  //数值越小的,越先执行
  int Order { get; }
}

IFilterFactory 也是过滤器

public interface IFilterMetadata
{
}

// IFilterFactory 也是过滤器
public interface IFilterFactory : IFilterMetadata
{
  bool IsReusable { get; }
  IFilterMetadata CreateInstance(IServiceProvider serviceProvider);
}

动态生成过滤器

public class MyFactoryFilter : IFilterFactory {
  public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) {
    return new MyActionFilter();
  }

  public bool IsReusable { get; }

  private class  MyActionFilter: IAsyncActionFilter {
    public async Task OnActionExecutionAsync(
      ActionExecutingContext context, ActionExecutionDelegate next) {
        Console.WriteLine("MyFactoryFilter before");
       await next();
       Console.WriteLine("MyFactoryFilter before");
    }
  }
}

筛选器里使用中间件

若要将中间件用作筛选器,可创建一个具有 Configure 方法的类型,该方法可指定要注入到筛选器管 道的中间件。 以下示例使用中间件设置响应标头资源筛选器的工作方式与中间件类似,即涵盖管道中 的所有后续执行。 但筛选器又不同于中间件,它们是运行时的一部分,这意味着它们有权访问上下文 和构造中间件筛选器与资源筛选器在筛选器管道的相同阶段运行,即,在模型绑定之前以及管道的其余 阶段之后。

public class FilterMiddlewarePipeline
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            context.Response.Headers.Add("Pipeline", "Middleware");

            await next();
        });
    }
}

[MiddlewareFilter(typeof(FilterMiddlewarePipeline))]
public class FilterMiddlewareController : Controller
{
    public IActionResult Index() =>
        Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}

asp.net core 约定 controller

asp.net core 也可以约定转换为 controller 通过添加 [Controller] 或 [ApiController] attribute 但是需要明确指定 [Route("test")] 及 [HttpPost("")] 的行为

[Route("test")]
[Controller]
/*[ApiController]*/
public class FarAppService {
  public FarAppService() {
  }

  [HttpPost("")]
  public async Task<bool> CreateFAsync() {
    return await Task.FromResult(true);
  }
}

IControllerActivator controller 生成器

namespace Microsoft.AspNetCore.Mvc.Controllers
{
  public class ControllerContext : ActionContext{}

  public interface IControllerActivator
  {
    //生成 controller
    object Create(ControllerContext context);
    //释放 controller
    void Release(ControllerContext context, object controller);
    ValueTask ReleaseAsync(ControllerContext context, object controller)
    {
      this.Release(context, controller);
      return new ValueTask();
    }
  }
}

通过 di 构造

public class ServiceBasedControllerActivator : IControllerActivator
{
    public object Create(ControllerContext actionContext)
    {
        if (actionContext == null)
        {
            throw new ArgumentNullException(nameof(actionContext));
        }

        var controllerType = actionContext.ActionDescriptor.ControllerTypeInfo.AsType();

        return actionContext.HttpContext.RequestServices.GetRequiredService(controllerType);
    }

    public virtual void Release(ControllerContext context, object controller)
    {
    }
}

通过工厂生成

namespace Microsoft.AspNetCore.Mvc.Infrastructure
{
  internal interface ITypeActivatorCache
  {
    TInstance CreateInstance<TInstance>(IServiceProvider serviceProvider, Type optionType);
  }
}

public delegate object ObjectFactory(IServiceProvider serviceProvider, object?[]? arguments);


internal sealed class TypeActivatorCache : ITypeActivatorCache
{
    // 通过 ActivatorUtilities.CreateFactory 生成对象,内部使用 emit 生成对象,
    // 在构造的时候有使用了 di
    private readonly Func<Type, ObjectFactory> _createFactory =
        // 生成 ObjectFactory 委托,是个 lambda 表达式树的委托
        (type) => ActivatorUtilities.CreateFactory(type, Type.EmptyTypes);

    //缓存生成委托
    private readonly ConcurrentDictionary<Type, ObjectFactory> _typeActivatorCache =
           new ConcurrentDictionary<Type, ObjectFactory>();


    public TInstance CreateInstance<TInstance>(
        IServiceProvider serviceProvider,
        Type implementationType)
    {
        if (serviceProvider == null)
        {
            throw new ArgumentNullException(nameof(serviceProvider));
        }

        if (implementationType == null)
        {
            throw new ArgumentNullException(nameof(implementationType));
        }

        //字典的 value 可以通过工厂生成  ObjectFactory
        var createFactory = _typeActivatorCache.GetOrAdd(implementationType, _createFactory);
        // ObjectFactory  执行 lambda 表达式生成实例
        return (TInstance)createFactory(serviceProvider, arguments: null);
    }
}
public static class ActivatorUtilities{
  public static ObjectFactory CreateFactory(
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
     Type instanceType,Type[] argumentTypes)
  {
      FindApplicableConstructor(instanceType, argumentTypes,
      out ConstructorInfo? constructor, out int?[]? parameterMap);

      ParameterExpression? provider =
      Expression.Parameter(typeof(IServiceProvider), "provider");
      ParameterExpression? argumentArray =
      Expression.Parameter(typeof(object[]), "argumentArray");
      Expression? factoryExpressionBody =
      BuildFactoryExpression(constructor, parameterMap, provider, argumentArray);

      var factoryLambda = Expression.Lambda<Func<IServiceProvider, object?[]?, object>>(
          factoryExpressionBody, provider, argumentArray);

      Func<IServiceProvider, object?[]?, object>? result = factoryLambda.Compile();
      return result.Invoke;
  }
}
internal sealed class DefaultControllerActivator : IControllerActivator
{
    private readonly ITypeActivatorCache _typeActivatorCache;

    public DefaultControllerActivator(ITypeActivatorCache typeActivatorCache)
    {
        if (typeActivatorCache == null)
        {
            throw new ArgumentNullException(nameof(typeActivatorCache));
        }

        _typeActivatorCache = typeActivatorCache;
    }


    public object Create(ControllerContext controllerContext)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException(nameof(controllerContext));
        }

        if (controllerContext.ActionDescriptor == null)
        {
            throw new ArgumentException(Resources.FormatPropertyOfTypeCannotBeNull(
                nameof(ControllerContext.ActionDescriptor),
                nameof(ControllerContext)));
        }

        var controllerTypeInfo = controllerContext.ActionDescriptor.ControllerTypeInfo;

        if (controllerTypeInfo == null)
        {
            throw new ArgumentException(Resources.FormatPropertyOfTypeCannotBeNull(
                nameof(controllerContext.ActionDescriptor.ControllerTypeInfo),
                nameof(ControllerContext.ActionDescriptor)));
        }

        var serviceProvider = controllerContext.HttpContext.RequestServices;
        return _typeActivatorCache.
        CreateInstance<object>(serviceProvider, controllerTypeInfo.AsType());
    }

    public void Release(ControllerContext context, object controller)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (controller == null)
        {
            throw new ArgumentNullException(nameof(controller));
        }

        if (controller is IDisposable disposable)
        {
            disposable.Dispose();
        }
    }

    public ValueTask ReleaseAsync(ControllerContext context, object controller)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (controller == null)
        {
            throw new ArgumentNullException(nameof(controller));
        }

        if (controller is IAsyncDisposable asyncDisposable)
        {
            return asyncDisposable.DisposeAsync();
        }

        Release(context, controller);
        return default;
    }
}
👍🎉🎊