ASP.NET Core 中文文档 第四章 MVC(4.3)过滤器

news2024/9/27 19:24:31

ASP.NET MVC 过滤器 可在执行管道的前后特定阶段执行代码。过滤器可以配置为全局有效、仅对控制器有效或是仅对 Action 有效。

查看或下载演示代码.

过滤器如何工作?

不同的过滤器类型会在执行管道的不同阶段运行,因此它们各自有一套适用场景。根据你实际要解决的问题以及在请求管道中执行的位置来选择创建不同的过滤器。运行于 MVC Action 调用管道内的过滤器有时被称为 过滤管道 ,当 MVC 选择要执行哪个 Action 后就会先执行该 Action 上的过滤器。
 

不同过滤器在管道的不同位置运行。像授权这样的过滤器只运行在管道的靠前位置,并且其后也不会跟随 action。其它过滤器(如 action 过滤器等)可以在管道的其它部分之前或之后执行,如下所示。
 

选择过滤器

授权过滤器 用于确定当前用户的请求是否合法。

资源过滤器 是授权之后第一个用来处理请求的过滤器,也是最后一个接触到请求的过滤器(因为之后就会离开过滤器管道)。在性能方面,资源过滤器在实现缓存或短路过滤器管道尤其有用。

Action 过滤器 包装了对单个 action 方法的调用,可以将参数传递给 action 并从中获得 action result。

异常过滤器 为 MVC 应用程序未处理异常应用全局策略。

结果过滤器 包装了单个 action result 的执行,当且仅当 action 方法成功执行完毕后方才运行。它们是理想的围绕视图执行或格式处理的逻辑(所在之处)。

实现

所有过滤器均可通过不同的接口定义来支持同步和异步实现。根据你所需执行的任务的不同来选择同步还是异步实现。从框架的角度来讲它们是可以互换的。

同步过滤器定义了 OnStageExecuting 方法和 OnStageExecuted 方法(当然也有例外)。OnStageExecuting 方法在具体事件管道阶段之前调用,而 OnStageExecuted 方法则在之后调用(比如当 Stage 是 Action 时,这两个方法便是 OnActionExecuting 和 OnActionExecuted,译者注)。

using FiltersSample.Helper;
using Microsoft.AspNetCore.Mvc.Filters;
namespace FiltersSample.Filters
{
    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
        }
    }
}

异步过滤器定义了一个 On\ Stage\ ExecutionAsync 方法,可以在具体管道阶段的前后运行。On\ Stage\ ExecutionAsync 方法被提供了一个 Stage\ ExecutionDelegate 委托,当调用时该委托会执行具体管道阶段的工作,然后等待其完成。

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;
namespace FiltersSample.Filters
{
    public class SampleAsyncActionFilter : IAsyncActionFilter
    {
        public async Task OnActionExecutionAsync(
            ActionExecutingContext context,
            ActionExecutionDelegate next)
        {
            // do something before the action executes
            await next();
            // do something after the action executes
        }
    }
}

注解

只能 实现一个过滤器接口,要么是同步版本的,要么是异步版本的,鱼和熊掌不可兼得 。如果你需要在接口中执行异步工作,那么就去实现异步接口。否则应该实现同步版本的接口。框架会首先检查是不是实现了异步接口,如果实现了异步接口,那么将调用它。不然则调用同步接口的方法。如果一个类中实现了两个接口,那么只有异步方法会被调用。最后,不管 action 是同步的还是异步的,过滤器的同步或是异步是独立于 action 的。

过滤器作用域

过滤器具有三种不同级别的 作用域 。你可以在特定的 action 上用特性(Attribute)的方式使用特定的过滤器;也可以在控制器上用特性的方式使用过滤器,这样就会将效果应用在控制器内所有的 action 上;或者注册一个全局过滤器,它将作用于整个 MVC 应用程序下的每一个 action。

如果想要使用全局过滤器的话,在你配置 MVC 的时候在 Startup 的 ConfigureServices 方法中添加:

过滤器可通过类型添加,也可以通过实例添加。如果通过实例添加,则该实例会被用于每一个请求。如果通过类型添加,则将会 type-activated(意思是说每次请求都会创建一个实例,其所有构造函数依赖项都将通过 DI 来填充)。通过类型添加过滤器相当于 filters.Add(new TypeFilterAttribute(typeof(MyFilter))) 。

把过滤器接口的实现当做 特性(Attributes) 使用是极为方便的。过滤器特性(filter attributes)可应用于控制器(Controllers)和 Action 方法。框架包含了内置的基于特性的过滤器,你可继承它们或另外定制。比方说,下例过滤器继承了 ResultFilterAttribute ,并重写(override)了 OnResultExecuting 方法(在响应中增加了一个头信息)。

特性允许过滤器接收参数,如下例所示。可将此特性加诸控制器(Controller)或 Action 方法,并为其指定所需 HTTP 头的名称和值,并将该 HTTP 头加入响应中:

[AddHeader("Author", "Steve Smith @ardalis")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using developer tools.");
    }
}

Index Action 的结果如下所示:响应的头信息显示在右下角。
 

以下几种过滤器接口可以自定义为相应特性的实现。

过滤器特性:

  • ActionFilterAttribute
  • ExceptionFilterAttribute
  • ResultFilterAttribute
  • FormatFilterAttribute
  • ServiceFilterAttribute
  • TypeFilterAttribute

取消与短路

通过设置传入过滤器方法的上下文参数中的 Result 属性,可以在过滤器管道的任意一点短路管道。比方说,下面的 ShortCircuitingResourceFilter 将阻止所有它之后管道内的所有过滤器,包括所有 action 过滤器。

using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace FiltersSample.Filters
{
    public class ShortCircuitingResourceFilterAttribute : Attribute,
            IResourceFilter
    {
        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            context.Result = new ContentResult()
            {
                Content = "Resource unavailable - header should not be set"
            };
        }
        public void OnResourceExecuted(ResourceExecutedContext context)
        {
        }
    }
}

 在下例中,ShortCircuitingResourceFilter 和 AddHeader 过滤器都指向了名为 SomeResource 的 action 方法。然而,由于首先运行的是 ShortCircuitingResourceFilter ,它短路了剩下的管道,SomeResource 上的 AddHeader 过滤器不会运行。如果这两个过滤器都以 Action 方法级别出现,它们的结果也会是一样的(只要 ShortCircuitingResourceFilter 首先运行,请查看 Ordering )。

配置过滤器

全局过滤器在 Startup.cs 中配置。基于特性的过滤器如果不需要任何依赖项的话,可以简单地继承一个与已存在的过滤器相对应的特性类型。如果要创建一个  全局作用域、但需要从依赖注入(DI)中获得依赖项的过滤器,在它们上面加上 ServiceFilterAttribute 或 TypeFilterAttribute 特性,这样就可用于控制器或 action 了。

依赖注入

以特性形式实现的、直接添加到控制器(Controller)类或 Action 方法的过滤器,其构造函数不得由 依赖注入 (DI)提供依赖项。其原因在于特性所需的构造函数参数必须由使用处直接提供。这是特性原型机理的限制。

不过,如果过滤器需要从 DI 中获得依赖项,那么有几种办法可以实现,可在类(class)或 Action 方法使用:

  • ServiceFilterAttribute
  • TypeFilterAttribute
  • IFilterFactory 实现你的特性

TypeFilter 将为其依赖项从 DI 中使用服务(services)来实例化一个实例。ServiceFilter 则从 DI 中取回一个过滤器实例。下例中将演示如何使用 ServiceFilter

[ServiceFilter(typeof(AddHeaderFilterWithDi))]
public IActionResult Index()
{
    return View();
}

如果在 ConfigureServices 中直接使用未经注册的 ServiceFilter 过滤器,则会抛出以下异常:

System.InvalidOperationException: No service for type
'FiltersSample.Filters.AddHeaderFilterWithDI' has been registered.

为避免此异常,你必须在 ConfigureServices 中为 AddHeaderFilterWithDI 类型注册:

services.AddScoped<AddHeaderFilterWithDi>();

ServiceFilterAttribute 实现了 IFilterFactory 接口,后者暴露了创建 IFilter 实例的单一方法。在 ServiceFilterAttribute 中,接口 IFilterFactory 中定义的 CreateInstance 方法被实现为用于从服务容器(DI)加载指定类型。

TypeFilterAttribute 很像 ServiceFilterAttribute (它同样是 IFilterFactory 的实现),但此类型并非直接解析自 DI 容器。

相反,它通过使用 Microsoft.Extensions.DependencyInjection.ObjectFactory 来实例化类型。

由于这种不同,使用 TypeFilterAttribute 引用的类型不需要在使用之前向容器注册(但它们依旧将由容器来填充其依赖项)。同样地,TypeFilterAttribute 能可选地接受该类型的构造函数参数。下例演示如何向使用 TypeFilterAttribute 修饰的类型传递参数:

[TypeFilter(typeof(AddHeaderAttribute),
    Arguments = new object[] { "Author", "Steve Smith (@ardalis)" })]
public IActionResult Hi(string name)
{
    return Content($"Hi {name}");
}

若是你有一个简单的不需要任何参数的、但其构造函数需要通过 DI 填充依赖项的过滤器的话,你可以通过继承 TypeFilterAttribute,在类(class)或方法(method)上使用自己命名的特性(来取代 [TypeFilter(typeof(FilterType))])。下例过滤器向你展示这是如何实现的:

public class SampleActionFilterAttribute : TypeFilterAttribute
{
    public SampleActionFilterAttribute():base(typeof(SampleActionFilterImpl))
    {
    }
    private class SampleActionFilterImpl : IActionFilter
    {
        private readonly ILogger _logger;
        public SampleActionFilterImpl(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<SampleActionFilterAttribute>();
        }
        public void OnActionExecuting(ActionExecutingContext context)
        {
            _logger.LogInformation("Business action starting...");
            // perform some business logic work
        }
        public void OnActionExecuted(ActionExecutedContext context)
        {
            // perform some business logic work
            _logger.LogInformation("Business action completed.");
        }
    }
}

该过滤器可通过使用 [SampleActionFilter] 这样的语法应用于类或方法,而不必使用 [TypeFilter] 或 [ServiceFilter]

注解

应避免纯粹为记录日志而创建和使用过滤器,这是因为 内建的框架日志功能 应该已经提供了你所需的功能。如果你要把日志记录功能放入过滤器中,它应专注于业务领域或过滤器的具体行为,而不是 MVC Action 或框架事件。

IFilterFactory 实现了 IFilter。因此,在过滤器管道的任何地方 IFilterFactory 实例都可当做 IFilter 实例来使用。当框架准备调用过滤器,将试图把其强制转换为 IFilterFactory。如果转换成功,将通过调用 CreateInstance 方法创建即将被调用的 IFilter 实例。因为过滤器管道不需要在应用程序启动时显式设置了,所以这是一种非常灵活的设计。

你可以在自己的特性实现中实现 IFilterFactory 接口,以此来实现另一种创建过滤器的方法:

public class AddHeaderWithFactoryAttribute : Attribute, IFilterFactory
{
    // Implement IFilterFactory
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return new InternalAddHeaderFilter();
    }
    private class InternalAddHeaderFilter : IResultFilter
    {
        public void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(
                "Internal", new string[] { "Header Added" });
        }
        public void OnResultExecuted(ResultExecutedContext context)
        {
        }
    }

排序

过滤器可应用于 Action 方法、控制器(Controller,通过特性(attribute)的形式)或添加到全局过滤器集合中。其作用域通常还决定了其执行顺序。最靠近 Action 的过滤器首先执行;通常来讲通过重写行为而不是显式设置顺序来改变顺序。这有时被称为“俄罗斯套娃”,因为每一个作用范围都包裹了前一个作用范围,就像是 套娃 那般。

除了作用范围,过滤器还可以通过实现 IOrderedFilter 来重写它们的执行顺序。该接口只是简单地暴露了 int Order 属性,然后执行时根据该数字正排序(数字越小,越先执行)后依次执行过滤器。所有内建过滤器(包括 TypeFilterAttribute 和 ServiceFilterAttribute)都实现了 IOrderedFilter 接口,因此当你将过滤器以特性的方式用于类(class)或方法(method)时你可以指定每一个过滤器的执行顺序。默认情况下所有内建过滤器的 Order 属性值都为 0,因此作用范围就当做决定性的因素(除非存在不为 0 的 Order 值)。

每个继承自 Controller 基类的控制器(Controller)都包含 OnActionExecuting 和 OnActionExecuted 方法。这些方法为给定的 Action 包装了过滤器,它们分别在最先和最后运行。基于作用范围的顺序(假设没有为过滤器的 Order 设置任何值):

  1. The Controller OnActionExecuting
  2. The Global filter OnActionExecuting
  3. The Class filter OnActionExecuting
  4. The Method filter OnActionExecuting
  5. The Method filter OnActionExecuted
  6. The Class filter OnActionExecuted
  7. The Global filter OnActionExecuted
  8. The Controller OnActionExecuted

注解

当过滤器将启动运行、需要决定过滤器执行顺序时,IOrderedFilter 会向外宣布自己的作用范围。过滤器首先通过 order 来排序,然后通过作用范围来决定。如果不设置,则 Order 默认为 0。

为在基于作用范围的排序中修改默认值,你须在类一级(class-level)或方法一级(method-level)的过滤器上显式设置 Order 属性。比如为方法一级的特性增加 Order=-1

[MyFilter(Name = "Method Level Attribute", Order=-1)]

这种情况下,小于 0 的值将确保该过滤器在全局过滤器和类一级过滤器之前运行(假设它们的 Order 属性均未设置)。

新的排序可能是这样的:

  1. The Controller OnActionExecuting
  2. The Method filter OnActionExecuting
  3. The Global filter OnActionExecuting
  4. The Class filter OnActionExecuting
  5. The Class filter OnActionExecuted
  6. The Global filter OnActionExecuted
  7. The Method filter OnActionExecuted
  8. The Controller OnActionExecuted

注解

Controller 类的方法总是在所有过滤器之前和之后运行。这些方法并未实现为 IFilter 实现,同时它们不参与 IFilter 的排序算法。

授权过滤器

授权过滤器 控制对 action 方法的访问,也是过滤器管道中第一个被执行的过滤器。它们只有一个前置阶段,不像其它大多数过滤器支持前置阶段方法和后置阶段方法。只有当你使用自己的授权框架时才需要定制授权过滤器。谨记勿在授权过滤器内抛出异常,这是因为所抛出的异常不会被处理(异常过滤器也不会处理它们)。此时记录该问题或寻求其它办法。

更多请访问 Authorization

资源过滤器

资源过滤器 要么实现 IResourceFilter 接口,要么实现 IAsyncResourceFilter 接口,它们执行于大多数过滤器管道(只有 授权过滤器 在其之前运行,其余所有过滤器以及 Action 处理均出现在其 OnResourceExecuting 和 OnResourceExecuted 方法之间)。当你需要短路绝大多数正在进行的请求时,资源过滤器特别有用。资源过滤器的一个典型例子是缓存,如果响应已经被缓存,过滤器会立即将之置为结果以避免后续 Action 的多余操作过程。

上面所说的是一个 短路资源过滤器 的例子。下例是一个非常简单的缓存实现(请勿将之用于生产环境),只能与 ContentResult 配合使用,如下所示:

public class NaiveCacheResourceFilterAttribute : Attribute,
    IResourceFilter
{
    private static readonly Dictionary<string, object> _cache
                = new Dictionary<string, object>();
    private string _cacheKey;
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        _cacheKey = context.HttpContext.Request.Path.ToString();
        if (_cache.ContainsKey(_cacheKey))
        {
            var cachedValue = _cache[_cacheKey] as string;
            if (cachedValue != null)
            {
                context.Result = new ContentResult()
                { Content = cachedValue };
            }
        }
    }
    public void OnResourceExecuted(ResourceExecutedContext context)
    {
        if (!String.IsNullOrEmpty(_cacheKey) &&
            !_cache.ContainsKey(_cacheKey))
        {
            var result = context.Result as ContentResult;
            if (result != null)
            {
                _cache.Add(_cacheKey, result.Content);
            }
        }
    }
}

在 OnResourceExecuting 中,如果结果已经在静态字段缓存中,Result 属性将被设置到 context 上,同时 Action 被短路并返回缓存的结果。在 OnResourceExecuted 方法中,如果当前其请求的键未被使用过,那么 Result 就会被保存到缓存中,用于之后的请求。

如下所示,把这个过滤器用于类或方法之上:

[TypeFilter(typeof(NaiveCacheResourceFilterAttribute))]
public class CachedController : Controller
{
    public IActionResult Index()
    {
        return Content("This content was generated at " + DateTime.Now);
    }
}

Action 过滤器

Action 过滤器 要么实现 IActionFilter 接口,要么实现 IAsyncActionFilter 接口,它们可以在 action 方法执行的前后被执行。Action 过滤器非常适合放置诸如查看模型绑定结果、或是修改控制器或输入到 action 方法的逻辑。另外,action 过滤器可以查看并直接修改 action 方法的结果。

OnActionExecuting 方法在 action 方法执行之前运行,因此它可以通过改变 ActionExecutingContext.ActionArguments 来控制 action 的输入,或是通过 ActionExecutingContext.Controller 控制控制器(Controller)。OnActionExecuting 方法可以通过设置 ActionExecutingContext.Result 来短路 action 方法的操作及其后续的过滤器。OnActionExecuting 方法通过抛出异常也可以阻止 action 方法和后续过滤器的处理,但会当做失败(而不是成功)的结果来处理.

OnActionExecuted 方法在 action 方法执行之后才执行,并且可以通过 ActionExecutedContext.Result 属性查看或控制 action 的结果。如果 action 在执行时被其它过滤器短路,则 ActionExecutedContext.Canceled 将会被置为 true。如果 action 或后续的 action 过滤器抛出异常,则 ActionExecutedContext.Exception 会被设置为一个非空值。有效「处理」完异常后把 ActionExecutedContext.Exception 设置为 null,那么 ActionExectedContext.Result 会像从 action 方法正常返回值那样被处理。

对于 IAsyncActionFilter 接口来说,它的 OnActionExecutionAsync 方法结合了 OnActionExecuting 和 OnActionExecuted 的所有能力。调用 await next() 后,ActionExecutionDelegate 将会执行所有的后续 action 过滤器以及 action 方法,并返回 ActionExecutedContext 。

如果想要在 OnActionExecutionAsync 内部短路,那么就为 ActionExecutingContext.Result 分配一个结果实例,并且不要调用 ActionExecutionDelegate 即可。

异常过滤器

异常过滤器 实现了 IExceptionFilter 接口或 IAsyncExceptionFilter 接口。

异常过滤器用于处理「未处理异常」,包括发生在 Controller 创建及 模型绑定 期间出现的异常。它们只在管道内发生异常时才会被调用。它们提供了一个单一的位置实现应用程序内的公共异常处理策略。框架提供了抽象的 ExceptionFilterAttribute ,你根据自己的需要继承这个类。异常过滤器适用于捕获 MVC Action 内出现的异常,但它们不及错误处理中间件(error handling middleware)灵活。一般来讲优先使用中间件,只有在需要做一些基于所选 MVC Action 的、有别于错误处理的工作时才选择使用过滤器。

提示

对于应用程序中不同 action 需要使用不同的错误处理方式,并向 Views/HTML 暴露 API 端点或 action 的错误处理结果。API 端点用 JSON 返回错误信息,而基于视图的 action 则返回错误页面(HTML 页面)。

异常过滤器不应有两个事件(对于前置或后置而言),它们只实现 OnException (或 OnExceptionAsync )。以参数形式传入 OnException 的 ExceptionContext 包含了所发生的 Exception。如果把 context.Exception 设置为 null,其效果相当于你已处理该异常,所以该次请求会像没发生过异常那样继续处理(一般会返回 HTTP 200 OK 状态)。下例过滤器中使用定制的开发者错误视图来显示开发环境中应用程序所出现异常的详细信息:

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
namespace FiltersSample.Filters
{
    public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
    {
        private readonly IHostingEnvironment _hostingEnvironment;
        private readonly IModelMetadataProvider _modelMetadataProvider;
        public CustomExceptionFilterAttribute(
            IHostingEnvironment hostingEnvironment,
            IModelMetadataProvider modelMetadataProvider)
        {
            _hostingEnvironment = hostingEnvironment;
            _modelMetadataProvider = modelMetadataProvider;
        }
        public override void OnException(ExceptionContext context)
        {
            if (!_hostingEnvironment.IsDevelopment())
            {
                // do nothing
                return;
            }
            var result = new ViewResult {ViewName = "CustomError"};
            result.ViewData = new ViewDataDictionary(_modelMetadataProvider,context.ModelState);
            result.ViewData.Add("Exception", context.Exception);
            // TODO: Pass additional detailed data via ViewData
            context.ExceptionHandled = true; // mark exception as handled
            context.Result = result;
        }
    }
}

结果过滤器

实现了 IResultFilter 或 IAsyncResultFilter 接口的 结果过滤器 在 Action Result 执行体的周围执行。当 Action 或 Action 过滤器产生 Action 结果时,只有成功运行的才会执行结果过滤器。如果异常过滤器处理了异常,那么结果过滤器就不会运行——除非异常过滤器将异常设置为null(Exception = null)。

注解

正在执行的结果种类取决于相关 Action。MVC Action 所返回的 View 将包含 Razor(将其作为正在处理的 ViewResult 的一部分)。API 方法则将执行一些序列化工作作为其执行结果的一部分。了解更多请移步 action 结果。

结果过滤器适用于任何需要直接环绕 View 或格式化处理的逻辑。结果过滤器可以替换或更改 Action 结果(而后者负责产生响应)。

OnResultExecuting 方法运行于 Action 结果执行之前,故其可通过 ResultExecutingContext.Result 操作 Action 结果。如果将 ResultExecutingContext.Cancel 设置为 true,则 OnResultExecuting 方法可短路 Action 结果以及后续结果过滤器的执行。如果发生了短路,MVC 将不会修改响应,所以当发生短路时,为避免生成空响应,你一般应该直接去修改响应对象。如果在 OnResultExecuting 方法内抛出异常,那么也将阻止 Action 结果以及后续过滤器的执行,但会被当做失败结果(而非成功结果)。

OnResultExecuted 方法运行于 Action 结果执行之后。也就是说,如果没有抛出异常,响应可能就会被发送到客户端且不可再修改。如果 Action 结果在执行中被其它过滤器短路,则 ResultExecutedContext.Canceled 将被置为 true。如果 Action 结果或后续结果过滤器抛出异常,则 ResultExecutedContext.Exception 将被置为非空值(non-null value)。把 ResultExecutedContext.Exception 设置为 null 后会影响到异常的“处理”,这将阻止异常在之后的管道内被 MVC 重新抛出。如果在结果过滤器内处理异常,需要确定此处是否适合将某些数据写入响应中。如果 Action 结果在执行中途抛出异常,而 header 也已被更新到客户端,那么将没有任何可靠的机制来发送失败代码。

对于 IAsyncResultFilter 的 OnResultExecutionAsync 方法来讲,它具有 OnResultExecuting 和 OnResultExecuted 的功能。在 ResultExecutionDelegate 上调用 await next() 将执行后续的结果过滤器和 Action 结果,并返回 ResultExecutedContext。如果将 ResultExecutingContext.Cancel 值为 true 并不调用 ResultExectionDelegate,则将在内部短路 OnResultExecutionAsync

你可以覆盖内建的 ResultFilterAttribute 特性,创建定制的结果过滤器, AddHeaderAttribute 类便是一例结果过滤器。

提示

若你需要为响应增加 header,在 Action 结果执行前如是做。否则响应就会被发送到客户端,届时改之晚矣。故对于结果过滤器而言,为响应增加 header 需要在 OnResultExecuting 中处理(而不是在 OnResultExecuted 中)。

过滤器对比中间件

一般情况下,过滤器用于处理业务与应用程序的横切关注点。它的用法很像 中间件 。从能力上来讲过滤器酷似中间件,但过滤器的作用范围很大,因此允许你将它插入到应用程序中需要使用到它的场合中,比如在视图之前或在模型绑定之后。过滤器是 MVC 的一部分,可以访问 MVC 的上下文以及构造函数。比方说,中间件不能简单地直接察觉请求中模型验证是否生成了错误并对此作出响应,而过滤器却能做到。

如果想要尝试一下过滤器,可以下载、测试并修改样例 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/712321.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

攻防视角下的信息收集组合拳-蓝队视角

攻防视角下的信息收集组合拳-蓝队视角 背景蓝队角度一、攻击前的信息收集1、企业资产梳理2、企业敏感文件清理 二、被攻击后的信息收集1、入侵成功信息收集技巧2、入侵失败信息收集技巧-锁定攻击IP反制技巧 背景 红蓝对抗中&#xff0c;最为重要的就是信息收集&#xff0c;无论…

国外追踪水资源短缺的6幅干旱地图

今天为大家分享国外6个干旱地图&#xff0c;这些地图使我们能够通过最新信息了解干旱的严重程度和范围。 它们通过捕捉水的可用性等关键信息&#xff0c;以便我们能够主动规划和应对干旱的影响。 全球干旱信息系统(GDIS) 地图网址&#xff1a; https://experience.arcgis.co…

HarmonyOS/OpenHarmony应用开发-Stage模型UIAbility组件使用(二)

UIAbility组件启动模式 UIAbility的启动模式是指UIAbility实例在启动时的不同呈现状态。针对不同的业务场景&#xff0c;系统提供了三种启动模式&#xff1a; singleton&#xff08;单实例模式&#xff09; standard&#xff08;标准实例模式&#xff09; specified&#xff0…

青少年机器人技术一级核心知识点、水平要求和考试标准

青少年机器人技术的发展已成为当今世界科技领域中备受瞩目的一项技术。机器人技术的应用范围越来越广泛&#xff0c;不仅在工业制造、医疗、教育等领域中得到广泛应用&#xff0c;同时也成为了青少年学习STEM&#xff08;科学、技术、工程、数学&#xff09;的重要内容。 为了…

错误: 字段“datlastsysoid“不存在(Navicat报错)

公司项目用到Postgres数据库&#xff0c;可视化管理工具我一般用的Navicat&#xff0c;之前也没有怎么用过Postgres数据库&#xff0c;自然对应的pgAmin工具也不是用起来也不是很友好&#xff0c;所以偷懒用Navicat&#xff0c;连接成功后&#xff0c;报错如下&#xff1a; 查…

ssm 招聘信息管理系统-计算机毕设 附源码78049

ssm 招聘客户管理系统 摘 要 由于数据库和数据仓库技术的快速发展&#xff0c;招聘客户管理系统建设越来越向模块化、智能化、自我服务和管理科学化的方向发展。招聘客户系统对处理对象和服务对象&#xff0c;自身的系统结构&#xff0c;处理能力&#xff0c;都将适应技术发展的…

vue3+ ts引入天地图 搜索功能+地理区域选择

前言&#xff1a; 天地图为开发者提供应用程序开发接口和在线服务资源&#xff0c;可满足各类基于地理信息的应用开发需求。 引入 <script src"http://api.tianditu.gov.cn/api?v4.0&tk你注册的key" type"text/javascript"></script>使…

Spring容器

1. Spring核心容器介绍 问题&#xff1a;按照Bean名称获取Bean有什么弊端&#xff0c;按照Bean类型获取Bean有什么弊端&#xff1f; 1.1 创建容器 方式一&#xff1a;类路径加载配置文件 ApplicationContext ctx new ClassPathXmlApplicationContext("applicationCon…

14. 向用户推荐朋友收藏的商品

文章目录 题目需求思路一&#xff1a;使用 Except实现一实现二&#xff1a;使用 Except思路三&#xff1a;使用 full outer join where实现三实现四&#xff1a;使用 not in concat题目来源 题目需求 现请向所有用户推荐其朋友收藏但是用户自己未收藏的商品&#xff0c;请从…

阻抗计算,真的没有那么难!

问&#xff1a;什么是阻抗&#xff1f; 答&#xff1a;在具有电阻、电感和电容的电路里&#xff0c;对电路中的电流所起的阻碍作用叫做阻抗。 问&#xff1a;什么是阻抗匹配? 答&#xff1a;阻抗匹配是指信号源或者传输线跟负载之间达到一种适合的搭配。阻抗匹配主要有两点作用…

ubuntu git clone 失败

命令行报错如下&#xff1a; gitxxx.xx.com: Permission denied (publickey). fatal: 无法读取远程仓库。修改步骤&#xff1a; 1、.ssh权限配置问题 .ssh 文件夹权限 755 或 700 config 文件权限 644 id_rsa 文件权限 600 id_rsa.pub 文件权限 644 r&#xff1a;read 代表读…

VMware安装Centos7并初始化网络使外部可以访问

本文目录 1&#xff1a;下载镜像2&#xff1a;安装centos7虚拟机2.1:初始化虚拟机2.2 初始化系统2.3 修改虚拟机网络模式 3&#xff1a;配置VMware虚拟网络编辑器4&#xff1a;配置centos网络4.1 修改网络配置文件4.2 刷新网络配置 5 验证网络总结 1&#xff1a;下载镜像 进入…

Web3.0在互联网圈爆红,这个“新一轮”概念有哪些机会?

在互联网行业里&#xff0c;新概念和潮流层出不穷&#xff0c;每一个概念都伴随着一轮火热的讨论和关注。从区块链到元宇宙&#xff0c;互联网的发展一直在不断地推动着新的创新和变革。而如今&#xff0c;Web3.0 成为了最新的热点话题&#xff0c;引起了互联网、科技和金融领域…

vue_前后端分离-增删改操作

增加操作和修改操作: 两个操作放一个页面进行操作 使用插槽(scope.row)的方式获取列表中的每一行数据 <template slot-scope"scope"><el-buttonsize"mini"click"openEditDialog( scope.row)">编辑</el-button>在进行添加的…

迅为RK3568开发板Buildroot 系统自启动 QT 程序

本小节将讲解如何开机自启动 QT 程序。 在设置自启动 QT 程序之前&#xff0c;首先要编译好 QT 可执行程序&#xff0c;完成以下两步。 1、 已经根据 03_【北京迅为】itop-3568 开发板快速使用编译环境 ubuntu18.04 v1.0.doc 第 10 章节进行了 QT 程序的交叉编译 2、 将交叉…

华为OD机试真题B卷 Python 实现【内存资源分配】,附详细解题思路

目录 一、题目描述二、输入描述三、输出描述四、解题思路五、Python算法源码六、效果展示1、输入2、输出 3、说明4、如果改一下呢&#xff1f; 一、题目描述 有一个简易内存池&#xff0c;内存按照大小粒度分类&#xff0c;每个粒度有若干个可用内存资源&#xff0c;用户会进行…

django 使用channels 搭建websocket聊天程序

channels官方文档&#xff1a;Django Channels — Channels 4.0.0 documentation 效果如下&#xff1a; 主要实现功能 基于Django的认证的群聊 具体实现 当建立websocket的时候&#xff0c;建立之前是http消息&#xff0c;我们可以拿到http消息里面的cookie等信息进行认证&…

数字经济时代,高校该如何建设大数据实验实训室,培养高质量数智人才?

数智人才作为数字经济时代发展的重要支撑资源&#xff0c;人才质量、存量与储备量之争&#xff0c;成为综合国力和区位竞争的重要内容&#xff0c;而大数据与人工智能技术作为数字经济的主要驱动力&#xff0c;相关人才的培养尤其重要。教育部自2016年起&#xff0c;陆续批准设…

Maven中 排除依赖 exclusions

使用maven进行jar包依赖管理时&#xff0c;maven会自行管理jar包及其依赖链条&#xff0c;但往往会遇到依赖冲突问题&#xff0c;这时候就可以尝试使用exclusions来进行依赖管理 demo:排除tomcat 启用 jetty <dependency><groupId>org.springframework.boot</g…

map父子维护的ebom父子部件关系计算组套数量

map父子维护的ebom父子部件关系计算组套数量 使用ebom时候&#xff0c;把部件父子关系和安装数量维护在了map上面&#xff0c;要计算组套数量&#xff0c;需要递归从当前往上取出父级安装数量相乘&#xff0c;一直取到最顶层。 G的组套数量&#xff1a;GDB32212 E的组套数量&a…