编程语言
首页 > 编程语言> > Filters in ASP.NET Core(Net6之过滤器)

Filters in ASP.NET Core(Net6之过滤器)

作者:互联网

Filters in ASP.NET Core

如果觉得样式不好:跳转即可 (md文件复制过来有些样式会不一样)

原文地址:https://lifengying.site/archives/net6%E4%B9%8B%E8%BF%87%E6%BB%A4%E5%99%A8

Filters in ASP.NET Core

官网地址:https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-6.0&viewFallbackFrom=aspnetcore-2.2

Filter

ASP.NET Core 中的过滤器*允许代码在请求处理管道中的特定阶段之前或之后运行。

内置过滤器处理以下任务:

可以创建自定义过滤器来处理横切关注点。横切关注点的示例包括错误处理、缓存、配置、授权和日志记录。过滤器避免重复代码。例如,错误处理异常过滤器可以合并错误处理。

本文档适用于 Razor 页面、API 控制器和具有视图的控制器。过滤器不能直接与Razor 组件一起使用。过滤器只能在以下情况下间接影响组件:

过滤器的工作原理

筛选器在ASP.NET Core 操作调用管道(有时称为筛选器管道)中运行。过滤器管道在 ASP.NET Core 选择要执行的操作后运行:

 

 

 

 

过滤器类型

每个过滤器类型在过滤器管道的不同阶段执行:

下图显示了过滤器类型如何在过滤器管道中交互:

 

 

 

总结:

Authorization filters、Exception filters没有前后2种方法(仅限同步方法)

Resource filters、Action filters、Result filters 有前后2种方法(仅限同步方法)

如果是异步的,则与中间件类似

 before //等同于xxxExecuting
 await netx();//action 执行xxx方法
 after  //等同于xxxExecuted

Razor Pages 还支持Razor Page 过滤器,它们在 Razor Page 处理程序之前和之后运行。

例子:ActionFilters

同步
 using Microsoft.AspNetCore.Mvc.Filters;
 ​
 namespace FilterMvc.Filters
 {
     public class SampleActionFilter : IActionFilter
    {
         public void OnActionExecuting(ActionExecutingContext context)
        {
             // Do something before the action executes.
             Console.WriteLine("执行方法前做点什么呢?执行方法前");
        }
 ​
         public void OnActionExecuted(ActionExecutedContext context)
        {
             // Do something after the action executes.
             Console.WriteLine("执行方法后做点什么呢?执行方法后");
        }
    }
 }
异步:
 using Microsoft.AspNetCore.Mvc.Filters;
 ​
 namespace FilterMvc.Filters
 {
     public class SampleAsyncActionFilter : IAsyncActionFilter
    {
         public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
             // Do something before the action executes.
             Console.WriteLine("执行方法前做点什么呢?执行方法前");
             await next();
             // Do something after the action executes.
             Console.WriteLine("执行方法后做点什么呢?执行方法后");
        }
    }
 ​
 }

 

Multiple filter stages(复式filter)

多个过滤级的接口可以在单个类中实现。例如,ActionFilterAttribute类实现:

实现过滤器接口的同步异步版本,而不是两者。运行时首先检查过滤器是否实现了异步接口,如果是,则调用它。如果不是,它调用同步接口的方法。如果在一个类中同时实现异步和同步接口,则只调用 async 方法。使用ActionFilterAttribute等抽象类时,仅覆盖每个过滤器类型的同步方法或异步方法。

内置Attributes Filter (Built-in filter attributes)

ASP.NET Core 包括内置的基于属性的过滤器,可以进行子类化和自定义。例如,以下结果过滤器会在响应中添加一个标头:

 public class ResponseHeaderAttribute : ActionFilterAttribute
 {
     private readonly string _name;
     private readonly string _value;
 ​
     public ResponseHeaderAttribute(string name, string value) =>
        (_name, _value) = (name, value);
 ​
     public override void OnResultExecuting(ResultExecutingContext context)
    {
         context.HttpContext.Response.Headers.Add(_name, _value);
 ​
         base.OnResultExecuting(context);
    }
 }

属性允许过滤器接受参数,如前面的示例所示。将 应用于ResponseHeaderAttribute控制器或操作方法并指定 HTTP 标头的名称和值:

 [ResponseHeader("Filter-Header", "Filter Value")]
 public class ResponseHeaderController : ControllerBase
 {
     public IActionResult Index() =>
         Content("Examine the response headers using the F12 developer tools.");
 ​
     // ...
执行结果:

 

 

 

 

 using FilterMvc.Filters;
 using Microsoft.AspNetCore.Mvc;
 ​
 namespace FilterMvc.Controllers
 {
     public class ResponseHeaderController : Controller
    {
         /* public IActionResult Index()
           {
               return View();
           }
   */
        [ResponseHeader("Filter-Header", "Filter Value")]
         public IActionResult Index()
        {
            return Content("Examine the response headers using the F12 developer tools.");
        }
 ​
        [ResponseHeader("Another-Filter-Header", "Another Filter Value")]
         public IActionResult Multiple() =>
        Content("Examine the response headers using the F12 developer tools.");
 ​
    }
 }

该操作的响应Multiple包括以下标头:

一些过滤器接口具有相应的属性,可以用作自定义实现的基类。

Filter attributes:

 

 

 

 

Filter范围和执行顺序(Filter scopes and order of execution)

可以在以下三个范围*之一将过滤器添加到管道中:

当管道的特定阶段有多个过滤器时,范围确定过滤器执行的默认顺序。全局过滤器围绕着类过滤器,而后者又围绕着方法过滤器。

由于过滤器嵌套,过滤器的后代码以与**前代码相反的顺序运行。过滤顺序:

 

 

 

以下示例说明了过滤器方法为同步操作过滤器运行的顺序:

序列Filter scopeFilter method
1 Global OnActionExecuting
2 Controller OnActionExecuting
3 Action OnActionExecuting
4 Action OnActionExecuted
5 Controller OnActionExecuted
6 Global OnActionExecuted

可以通过实现IOrderedFilter来覆盖默认的执行顺序。IOrderedFilter公开Order属性,该属性优先于范围以确定执行顺序。具有较低Order值的过滤器:

控制器级别过滤器示例中,GlobalSampleActionFilter具有全局范围,因此它在SampleActionFilterAttribute具有控制器范围之前运行。要先运行,请将SampleActionFilterAttribute其顺序设置为int.MinValue

 [SampleActionFilter(Order = int.MinValue)]
 public class ControllerFiltersController : Controller
 {
     // ...
 }

GlobalSampleActionFilter要首先运行全局过滤器,请将其设置Orderint.MinValue

 builder.Services.AddControllersWithViews(options =>
 {
     options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
 });

顺序总结:(这个指的是同一级别的Filter)

order值越小越先执行,由于是before action after 所以越小的after越后执行,和中间件类似

能用中间件实现的功能,优先考虑中间件,如果在Mvc里面的需求再考虑Filter

原文地址:https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.filters.iorderedfilter.order?view=aspnetcore-6.0#microsoft-aspnetcore-mvc-filters-iorderedfilter-order

取消和短路

可以通过在提供给过滤器方法的ResourceExecutingContext参数上设置Result属性来短路过滤器管道。例如,以下资源过滤器会阻止管道的其余部分执行:

 public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
 {
     public void OnResourceExecuting(ResourceExecutingContext context)
    {
     //设置Result属性即可使ResourceFilter 之后的过滤器都不执行 在这里短路了
         context.Result = new ContentResult
        {
             Content = nameof(ShortCircuitingResourceFilterAttribute)
        };
    }
 ​
     public void OnResourceExecuted(ResourceExecutedContext context) { }
 }

 

在下面的代码中,[ShortCircuitingResourceFilter][ResponseHeader]filter 都以Indexaction 方法为目标。ShortCircuitingResourceFilterAttribute过滤器:

因此ResponseHeaderAttribute过滤器永远不会运行该Index操作。如果两个过滤器都在操作方法级别应用,则此行为将是相同的,前提是ShortCircuitingResourceFilterAttribute先运行。首先ShortCircuitingResourceFilterAttribute运行是因为它的过滤器类型:

 [ResponseHeader("Filter-Header", "Filter Value")]
 public class ShortCircuitingController : Controller
 {
    [ShortCircuitingResourceFilter]
     public IActionResult Index() =>
         Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
 }

依赖注入

过滤器可以按类型或实例添加。如果添加了一个实例,则该实例将用于每个请求。如果添加了类型,则它是类型激活的。类型激活过滤器意味着:

作为属性实现并直接添加到控制器类或操作方法的过滤器不能具有依赖注入(DI) 提供的构造函数依赖项。构造函数依赖项不能由 DI 提供,因为属性必须在应用它们的地方提供其构造函数参数。

以下过滤器支持 DI 提供的构造函数依赖项:

前面的过滤器可以应用于控制器或动作。

记录仪可从 DI 获得。但是,请避免纯粹出于日志记录目的而创建和使用过滤器。内置框架日志记录通常提供日志记录所需的内容。日志添加到过滤器:

服务过滤器属性(ServiceFilterAttribute)

服务过滤器实现类型注册在Program.cs. ServiceFilterAttribute从 DI 检索过滤器的实例。

以下代码显示了LoggingResponseHeaderFilterService使用 DI 的类:

 public class LoggingResponseHeaderFilterService : IResultFilter
 {
     private readonly ILogger _logger;
 ​
     public LoggingResponseHeaderFilterService(
             ILogger<LoggingResponseHeaderFilterService> logger) =>
         _logger = logger;
 ​
     public void OnResultExecuting(ResultExecutingContext context)
    {
         _logger.LogInformation(
             $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuting)}");
 ​
         context.HttpContext.Response.Headers.Add(
             nameof(OnResultExecuting), nameof(LoggingResponseHeaderFilterService));
    }
 ​
     public void OnResultExecuted(ResultExecutedContext context)
    {
         _logger.LogInformation(
             $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuted)}");
    }
 }

在以下代码中,LoggingResponseHeaderFilterService添加到 DI 容器中:

 builder.Services.AddScoped<LoggingResponseHeaderFilterService>();

在以下代码中,属性从 DIServiceFilter检索过滤器的实例:LoggingResponseHeaderFilterService

 [ServiceFilter(typeof(LoggingResponseHeaderFilterService))]
 public IActionResult WithServiceFilter() =>
     Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");

使用时ServiceFilterAttribute,设置ServiceFilterAttribute.IsReusable

ServiceFilterAttribute实现IFilterFactoryIFilterFactory公开用于创建IFilterMetadata实例的CreateInstance方法。从 DI 加载指定的类型。CreateInstance

总结

在要使用的地方如上述加上[ServiceFilter(typeof(LoggingResponseHeaderFilterService))]即可

执行结果:

 

 

类型过滤器属性(TypeFilterAttribute)

TypeFilterAttribute类似于ServiceFilterAttribute,但它的类型不是直接从 DI 容器解析的。它使用Microsoft.Extensions.DependencyInjection.ObjectFactory实例化类型。

因为TypeFilterAttribute类型不是直接从 DI 容器中解析的:

使用时TypeFilterAttribute,设置TypeFilterAttribute.IsReusable

以下示例显示了如何使用 将参数传递给类型TypeFilterAttribute

 [TypeFilter(typeof(LoggingResponseHeaderFilter),
     Arguments = new object[] { "Filter-Header", "Filter Value" })]
 public IActionResult WithTypeFilter() =>
     Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");

执行结果:

 

 

过滤器工厂(IFilterFactory)

IFilterFactory实现IFilterMetadata。因此,IFilterFactory实例可以用作IFilterMetadata过滤器管道中任何位置的实例。当运行时准备调用过滤器时,它会尝试将其转换为IFilterFactory. 如果该转换成功,则调用CreateInstance方法来创建所IFilterMetadata调用的实例。这提供了一种灵活的设计,因为在应用程序启动时不需要明确设置精确的过滤器管道。

IFilterFactory.IsReusable

ASP.NET Core 运行时不保证:

警告

仅当过滤器的来源明确、过滤器是无状态的并且过滤器可以安全地跨多个 HTTP 请求使用时,才将IFilterFactory.IsReusable配置为返回。例如,如果返回true,则不要从注册为作用域或瞬态的 DI 中返回过滤器。IFilterFactory.IsReusable``true

IFilterFactory可以使用自定义属性实现作为创建过滤器的另一种方法来实现:

 public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
 {
     public bool IsReusable => false;
 ​
     public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
         new InternalResponseHeaderFilter();
 ​
     private class InternalResponseHeaderFilter : IActionFilter
    {
         public void OnActionExecuting(ActionExecutingContext context) =>
             context.HttpContext.Response.Headers.Add(
                 nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));
 ​
         public void OnActionExecuted(ActionExecutedContext context) { }
    }

过滤器应用在以下代码中:

 [ResponseHeaderFilterFactory]
 public IActionResult Index() =>
     Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");

总结:

IFilterFactory.IsReusable(可复用) 这个属性直接关系到生命周期以及使用场景。根据自己具体项目来看

True则时单例,不能与非单例的Filter一起使用。

IFilterFactory 在属性(Attribute)上实现

实现IFilterFactory的过滤器对于以下过滤器很有用:

TypeFilterAttribute实现IFilterFactoryIFilterFactory公开用于创建IFilterMetadata实例的CreateInstance方法。从服务容器 (DI) 加载指定的类型。CreateInstance

 public class SampleActionTypeFilterAttribute : TypeFilterAttribute
 {
     public SampleActionTypeFilterAttribute()
          : base(typeof(InternalSampleActionFilter)) { }
 ​
     private class InternalSampleActionFilter : IActionFilter
    {
         private readonly ILogger<InternalSampleActionFilter> _logger;
 ​
         public InternalSampleActionFilter(ILogger<InternalSampleActionFilter> logger) =>
             _logger = logger;
 ​
         public void OnActionExecuting(ActionExecutingContext context)
        {
             _logger.LogInformation(
                 $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuting)}");
        }
 ​
         public void OnActionExecuted(ActionExecutedContext context)
        {
             _logger.LogInformation(
                 $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuted)}");
        }
    }
 }

以下代码显示了三种应用过滤器的方法:

 [SampleActionTypeFilter]//推荐使用这种
 public IActionResult WithDirectAttribute() =>
     Content($"- {nameof(FilterFactoryController)}.{nameof(WithDirectAttribute)}");
 ​
 [TypeFilter(typeof(SampleActionTypeFilterAttribute))]
 public IActionResult WithTypeFilterAttribute() =>
     Content($"- {nameof(FilterFactoryController)}.{nameof(WithTypeFilterAttribute)}");
 ​
 [ServiceFilter(typeof(SampleActionTypeFilterAttribute))]
 public IActionResult WithServiceFilterAttribute() =>
     Content($"- {nameof(FilterFactoryController)}.{nameof(WithServiceFilterAttribute)}");

在前面的代码中,首选应用过滤器的第一种方法。

在过滤器管道中使用中间件

资源过滤器像中间件一样工作,因为它们围绕管道中稍后出现的所有内容的执行。但是过滤器与中间件的不同之处在于它们是运行时的一部分,这意味着它们可以访问上下文和构造。

要将中间件用作过滤器,请使用Configure指定要注入过滤器管道的中间件的方法创建一个类型。以下示例使用中间件设置响应标头:

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

使用MiddlewareFilterAttribute运行中间件:

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

中间件过滤器与资源过滤器在过滤器管道的同一阶段运行,在模型绑定之前和管道的其余部分之后。

执行结果

 

 

线程安全

When passing an instance of a filter into Add, instead of its Type, the filter is a singleton and is not thread-safe.

当将过滤器的实例Add传递给而不是其Type时,过滤器是单例并且不是线程安全的。(机翻)

例子: T t = new T();

Add(t);

授权过滤器

授权过滤器:

自定义授权过滤器需要自定义授权框架。优先配置授权策略或编写自定义授权策略而不是编写自定义过滤器。内置授权过滤器:

不要在授权过滤器中抛出异常

考虑在授权过滤器中发生异常时发出质询。

了解有关授权的更多信息。

资源过滤器

资源过滤器:

资源过滤器可用于使大部分管道短路。例如,缓存过滤器可以避免缓存命中的其余管道。

资源过滤器示例:

动作过滤器

操作过滤器不适用于 Razor 页面。Razor Pages 支持IPageFilterIAsyncPageFilter。有关详细信息,请参阅Razor 页面的筛选方法

动作过滤器:

以下代码显示了一个示例操作过滤器:

 public class SampleActionFilter : IActionFilter
 {
     public void OnActionExecuting(ActionExecutingContext context)
    {
         // Do something before the action executes.
    }
 ​
     public void OnActionExecuted(ActionExecutedContext context)
    {
         // Do something after the action executes.
    }
 }

ActionExecutingContext提供以下属性:

在操作方法中抛出异常:

ActionExecutedContext提供Controller加上Result以下属性:

对于一个IAsyncActionFilter,调用ActionExecutionDelegate

要短路,请将Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result分配给结果实例并且不要调用next( ActionExecutionDelegate)。

该框架提供了一个可以被子类化的抽象ActionFilterAttribute 。

动作过滤器OnActionExecuting可用于:

public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}

[ApiController]使用该属性注释的控制器会自动验证模型状态并返回 400 响应。有关详细信息,请参阅自动 HTTP 400 响应

OnActionExecuted方法在操作方法之后运行:

异常过滤器

异常过滤器:

以下示例异常筛选器显示有关在开发应用程序时发生的异常的详细信息:

public class SampleExceptionFilter : IExceptionFilter
{
    private readonly IHostEnvironment _hostEnvironment;

    public SampleExceptionFilter(IHostEnvironment hostEnvironment) =>
        _hostEnvironment = hostEnvironment;

    public void OnException(ExceptionContext context)
    {
        if (!_hostEnvironment.IsDevelopment())
        {
            // Don't display exception details unless running in Development.
            return;
        }

        context.Result = new ContentResult
        {
            Content = context.Exception.ToString()
        };
    }
}

以下代码测试异常过滤器:

 [TypeFilter(typeof(SampleExceptionFilter))]
 public class ExceptionController : Controller
 {
     public IActionResult Index() =>
         Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
 }

异常过滤器:

要处理异常,请将ExceptionHandled属性设置为true或分配Result属性。这会阻止异常的传播。异常过滤器不能将异常转化为“成功”。只有动作过滤器可以做到这一点。

异常过滤器:

首选中间件进行异常处理。仅在错误处理因调用的操作方法而异时才使用异常过滤器。例如,一个应用程序可能具有 API 端点和视图/HTML 的操作方法。API 端点可以以 JSON 形式返回错误信息,而基于视图的操作可以以 HTML 形式返回错误页面。

结果过滤器

结果过滤器:

IResultFilter 和 IAsyncResultFilter

以下代码显示了一个示例结果过滤器:

 public class SampleResultFilter : IResultFilter
 {
     public void OnResultExecuting(ResultExecutingContext context)
    {
         // Do something before the result executes.
    }
 ​
     public void OnResultExecuted(ResultExecutedContext context)
    {
         // Do something after the result executes.
    }
 }

执行的结果类型取决于操作。返回视图的操作包括所有 razor 处理作为正在执行的ViewResult的一部分。作为结果执行的一部分,API 方法可能会执行一些序列化。了解有关行动结果的更多信息。

结果过滤器仅在操作或操作过滤器产生操作结果时执行。在以下情况下不执行结果过滤器:

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting方法可以通过将Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel设置为来短路操作结果和后续结果筛选器的执行true。短路时写入响应对象以避免生成空响应。抛出异常IResultFilter.OnResultExecuting

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted方法运行时,响应可能已经发送到客户端。如果响应已经发送到客户端,则无法更改。

ResultExecutedContext.Canceled``true如果动作结果执行被另一个过滤器短路,则设置为。

ResultExecutedContext.Exception如果操作结果或后续结果过滤器抛出异常,则设置为非空值。设置Exception为 null 可以有效地处理异常并防止在管道中稍后再次抛出异常。在结果过滤器中处理异常时,没有可靠的方法将数据写入响应。如果在操作结果引发异常时已将标头刷新到客户端,则没有可靠的机制来发送失败代码。

对于IAsyncResultFilterawait next ,对ResultExecutionDelegate的调用将执行任何后续结果过滤器和操作结果。要短路,请将ResultExecutingContext.Cancel设置为true并且不要调用ResultExecutionDelegate

 public class SampleAsyncResultFilter : IAsyncResultFilter
 {
     public async Task OnResultExecutionAsync(
         ResultExecutingContext context, ResultExecutionDelegate next)
    {
         if (context.Result is not EmptyResult)
        {
             await next();
        }
         else
        {
             context.Cancel = true;
        }
    }
 }

ResultFilterAttribute该框架提供了一个可以被子类化的抽象。前面显示的ResponseHeaderAttribute类是结果过滤器属性的一个示例。

IAlwaysRunResultFilter 和 IAsyncAlwaysRunResultFilter

IAlwaysRunResultFilter和IAsyncAlwaysRunResultFilter接口声明了一个针对所有操作结果运行的IResultFilter实现。这包括由以下人员产生的行动结果:

例如,当内容协商失败时,以下过滤器始终运行并设置带有422 Unprocessable Entity状态代码的操作结果 ( ObjectResult ):

 public class UnprocessableResultFilter : IAlwaysRunResultFilter
 {
     public void OnResultExecuting(ResultExecutingContext context)
    {
         if (context.Result is StatusCodeResult statusCodeResult
             && statusCodeResult.StatusCode == StatusCodes.Status415UnsupportedMediaType)
        {
             context.Result = new ObjectResult("Unprocessable")
            {
                 StatusCode = StatusCodes.Status422UnprocessableEntity
            };
        }
    }
 ​
     public void OnResultExecuted(ResultExecutedContext context) { }
 }

 

 

 

 

以下是图片

 

标签:Core,ASP,nameof,context,class,Filters,过滤器,执行,public
来源: https://www.cnblogs.com/firstsaofan/p/16644724.html