ASP.NET Core应用程序13:使用过滤器

【AOP】ASP.NET Core中各种Filter(拦截器) 在ASP.NET Core中,Filter(过滤器)是一种强大的机制,允许你在请求处理管道中的特定阶段运行代码。这些过滤器提供了在请求的不同阶段执行逻辑的能力,比如授权、日志记录、异常处理等。ASP.NET Core 提供了多种类型的过滤器,每种类型都在请求处理管道的不同阶段运行。 阅读详情

  过滤器将额外的逻辑注入请求处理。过滤器类似于应用于单个端点的中间件,可以是操作或页面处理程序方法,它们提供了一种管理特定请求集的优雅方法。
  本章描述 ASP.NET Core 过滤器特性,并解释如何使用它来更改特定端点的请求和结果,描述不同类型的过滤器,演示了如何创建和应用每种过滤器。还展示了如何管理过滤器的生命周期并控制它们的执行顺序。

1 准备工作

  本章使用了上章项目。
  删除控制器、视图和 Razor Pages,留下共享布局、数据模型和配置文件。
  重新添加 Controllers 文件夹,添加 HomeController.cs。

public class HomeController : Controller
{
    private DataContext _context;
    public HomeController(DataContext ctx)
    {
        _context = ctx;
    }
    public async Task<IActionResult> Index()
    {
        return View("Message","这是home控制器的Index方法");
    }
}

  操作方法呈现一个名为 Message 的视图,并传递一个字符串作为视图数据。Views/Shared 添加一个名Message.cshtml 的 Razor 视图。

@{
    Layout = "_SimpleLayout";
}

@if (Model is string)
{
    @Model
}
else if (Model is IDictionary<string, string>)
{
    var dict = Model as IDictionary<string, string>;
    <table class="table table-sm table-striped table-bordered">
        <thead><tr><th>Name</th><th>Value</th></tr></thead>
        <tbody>
            @foreach (var kvp in dict)
            {
                <tr><td>@kvp.Key</td><td>@kvp.Value</td></tr>
            }
        </tbody>
    </table>
}

  在 Pages 文件夹中添加一个名为 Message.cshtml 的 Razor Pages。

@page "/pages/message"
@model MessageModel
@using Microsoft.AspNetCore.Mvc.RazorPages
@using System.Collections.Generic


@if (Model.Message is string)
{
    @Model.Message
}
else if (Model.Message is IDictionary<string, string>)
{
    var dict = Model.Message as IDictionary<string, string>;
    <table class="table table-sm table-striped table-bordered">
        <thead><tr><th>Name</th><th>Value</th></tr></thead>
        <tbody>
            @foreach (var kvp in dict)
            {
                <tr><td>@kvp.Key</td><td>@kvp.Value</td></tr>
            }
        </tbody>
    </table>
}

@functions
{
    public class MessageModel : PageModel
    {
        public object Message { get; set; } = $"This is the Message Razor Page";

    }
}

启用 HTTPS 连接
  本章中的一些示例需要使用 SSL。修改 Properties 文件夹的 launchSettings,json 文件中,来启用 SSL 和 HTTPS,并将端口设置为 44350。

{
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "MyWebApp": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "applicationUrl": "http://localhost:5000;http://localhost:44350"

    }
  },
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:5000",
      "sslPort": 44350
    }
  }
}

  请求 http://localhost:5000/pages/message 和 https://localhost:44350/pages/message以查看通过 HTTP 和 HTTPS 传递的、来自 Message.cshtml Razor Pages 的响应。

2 使用过滤器

  假设想对一些操作方法强制执行 HTTPS 请求。中间件中通过读取 HttpRequest 对象的 IsHtps 属性来实现这一点if(Request.IsHttps),但更好的方式是在类或方法上使用特性[RequireHttps]过滤器。

[RequireHttps]
public class HomeController : Controller

3 理解过滤器

  ASP.NET Core 支持不同类型的过滤器,每种过滤器用于不同的目的。下表描述了过滤器类别:

名称描述
授权过滤器这种类型的过滤器用于应用应用程序的授权策略
资源过滤器这种类型的过滤器用于拦截请求,通常用于实现诸如缓存的特性
操作过滤器这种类型的过滤器用于在请求被操作方法接收之前修改请求,或者在生成请求之后修改操作结果。
这种类型的过滤器只能应用于控制器和操作
页面过滤器这种类型的过滤器用于在 Razor Pages 处理程序方法接收到请求之前修改请求,或者在生成操作
结果之后修改操作结果。这种类型的过滤器只能应用于 Razor Pages
结果过滤器此类型的过滤器用于在操作执行前更改操作结果,或在执行后修改结果
异常过滤器这种类型的过滤器用于处理操作方法或页面处理程序执行期间发生的异常

  过滤器有自己的管道,并按照特定的顺序执行。过滤器可以短路过滤器管道,以阻止请求被转发到下一个过滤器。image.png
  每种类型的过滤器都是使用 ASP.NET Core 定义的接口实现的。它还提供了一些基类,可以方便地将某些类型的过滤器应用为属性。
  过滤器类型、接口和特性基类:

过滤器类型接口特性类
授权过滤器IAuthorizationFilter、IAsyncAuthorizationFilter无
资源过滤器IResourceFilter、IAsyncResourceFilter无
操作过滤器IActionFilter、 IAsyncActionFilterActionFilterAttribute
页面过滤器IPageFilter、IAsyncPageFilter无
结果过滤器IResultFilter、IAsyncResultFilter、
IAlwaysRunResultFilter、IAsyncAlwaysRunResultFilter
ResultFilterAttribute
异常过滤器lexceptionFilter、 IAsyncExceptionFilterExceptionFilterAttribute

4 创建自定义过滤器

4.1 授权过滤器

  授权过滤器用于实现应用程序的安全策略。授权过滤器在其他类型的过滤器和端点处理之前执行。以下是 IAuthorizationFilter 接口的定义:

namespace Microsoft.AspNetCore.Mvc.Filters
{
    public interface IAuthorizationFilter :IFilterMetadata
    {
        void OnAuthorization (AuthorizationFilterContext context);
    }
}

  调用 OnAuthorization 方法,为过滤器提供授权请求的机会,也有异步版本。

public interface IAsyncAuthorizationFilter : IFilterMetadata
{
    Task OnAuthorizationAsync(AuthorizationFilterContext context);
}

  过滤器会通过 AuthorizationFilterContext 对象接收描述请求的上下文数据,该对象是从 FilterContext 类派生出来的,并添加了一个重要属性 Result,当请求不符合应用程序的授权策略时,授权过滤器会设置这个IActionResult 属性。创建授权过滤器
  创建 Filters 文件夹,并添加了一个名为 HttpsOnlyAttribute.cs 的文件,并使用它定义过滤器。

public class HttpsOnlyAttribute : Attribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        if (!context.HttpContext.Request.IsHttps)
        {
            context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
        }
    }
}

  如果请求符合授权策略,授权过滤器就不执行任何操作,同时允许 ASP.NET Core 移到下一个过滤器,并最终执行端点。如果存在问题,过滤器将设置传递给 OnAuthorization 方法 AuthorizationFilterContext 对象的 Result 属性。这将防止进一步执行操作,并提供返回给客户端的结果。
  HomeController.cs 文件中应用自定义过滤器。

[HttpsOnly]
public class HomeController : Controller

  请求 http://localhost:5000 和 https://localhost:44350,前者将被拦截。

4.2 资源过滤器

  资源过滤器对每个请求执行两次模型绑定过程,在操作结果处理之前再生成结果。以下是IResourceFilter 接口的定义:

namespace Microsoft.AspNetCore.Mvc.Filters 
{
    public interface IResourcerilter :IFilterMetadata
    {
        void OnResourceExecuting(ResourceExecutingContext context);
        void OnResourceExecuted(ResourceExecutedContext context);
    }
}

  OnResourceExecuting 方法在处理请求时调用,而 OnResourceExecuted 方法在端点处理了请求之后、在操作结果执行之前调用。
  异步版本如下。

public interface IAsyncResourceFilter : IFilterMetadata
{
    Task OnResourceExecutionAsync(ResourceExecutingContext context, 
                                  ResourceExecutionDelegate next);
}

(1)创建资源过滤器
  资源过滤器通常用于可能使管道短路并尽早提供响应的地方,例如在实现数据缓存时。要创建一个简单的缓存过滤器,将一个名为 SimpleCacheAttribute.cs 的类文件添加到 Filters 文件夹。

public class SimpleCacheAttribute : Attribute, IResourceFilter
{
    private Dictionary<PathString, IActionResult> CachedResponses
        = new Dictionary<PathString, IActionResult>();
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        PathString path = context.HttpContext.Request.Path;
        if (CachedResponses.ContainsKey(path))
        {
            context.Result = CachedResponses[path];
            CachedResponses.Remove(path);
        }
    }
    public void OnResourceExecuted(ResourceExecutedContext context)
    {
        CachedResponses.Add(context.HttpContext.Request.Path, context.Result);
    }
}

  OnResourceExecution 方法通过将上下文对象的 Result 属性设置为先前缓存的操作结果,提供了短路管道的机会。如果为 Result 属性分配了一个值,则过滤器管道将短路,并执行操作结果来为客户端生成响应。缓存的操作结果只使用一次,然后从缓存中丢弃。如果没有为 Result 属性分配值,则请求传递到管道中的下一个步骤,该步骤可能是另一个过滤器或端点。
  将自定义资源过滤器应用到 Message.cshtml Razor Pages,并添加一个时间截,时间戳将帮助确定何时缓存操作结果。

@functions
{
    [SimpleCache]
    public class MessageModel : PageModel
    {
        public object Message { get; set; } = 
            $"{DateTime.Now.ToLongTimeString()}:This is the Message Razor Page";s
    }
}

  请求 https://localhost:44350/pages/message。因为这是对路径的第一个请求,所以不会缓存结果,请求将沿着管道转发。在处理响应时,资源过滤器将缓存操作结果供将来使用。重新加载浏览器以重复请求,会显示相同的时间戳,表明己使用了缓存的操作结果。缓存项在使用时被删除,这意味着重新加载浏览器将生成带有新时间戳的响应。(2)创建异步资源过滤器
  异步资源过滤器的接口使用单个方法来接收用于沿着过滤器管道转发请求的委托。以下重新实现了前一个示例中的缓存过滤器,以便它实现 IAsyncResourceFilter 接口。

public class SimpleCacheAttribute : Attribute, IAsyncResourceFilter
{
    private Dictionary<PathString, IActionResult> CachedResponses
        = new Dictionary<PathString, IActionResult>();
    public async Task OnResourceExecutionAsync(ResourceExecutingContext context,
            ResourceExecutionDelegate next)
    {
        PathString path = context.HttpContext.Request.Path;
        if (CachedResponses.ContainsKey(path))
        {
            context.Result = CachedResponses[path];
            CachedResponses.Remove(path);
        }
        else
        {
            ResourceExecutedContext execContext = await next();
            CachedResponses.Add(context.HttpContext.Request.Path, execContext.Result);
        }
    }
}

4.3 操作过滤器

  与资源过滤器一样,操作过滤器执行两次。区别在于操作过滤器在模型绑定过程之后执行而资源过滤器在模型绑定之前执行。下面是 IActionFilter 接口:

namespace Microsoft.AspNetCore.Mvc.Filters
{
    public interface IActionrilter :IFilterMetadata
    {
        void OnActionExecuting(ActionExecutingContext context);
        void OnActionExecuted(ActionExecutedContext context);
    }
}

  异步操作过滤器是使用 IAsyncActionFilter 接口实现的。

namespace Microsoft.AspNetCore.Mvc.Filters 
{
    public interface IAsyncActionrilter :IFilterMetadata
    {
        Task OnActionExecutionAsync (ActionExecutingContext context,
                                     ActionExecutionDelegate next);
    }
}

(1)创建操作过滤器
  在 Filters 文件夹中添加一个名为 ChangeArgAttribute.cs 的类文件,并使用它来定义操作过滤器。

public class ChangeArgAttribute : Attribute, IActionActionFiler
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context,
            ActionExecutionDelegate next)
    {
        if (context.ActionArguments.ContainsKey("message1"))
        {
            context.ActionArguments["message1"] = "New message";
        }
        await next();
    }
}

  过滤器査找名为 message1 的操作参数,并更改用于调用操作方法的值。用于操作方法参数的值由模型绑定过程决定。 如下向 Home 控制器添加了一个操作方法,并应用了新的过滤器。

[ChangeArg]
public IActionResult Messages(string message1, string message2 = "Nome")
{
    return View("Message", $"{message1},{message2}");
}

  请求https://localhost:44350/home/messages?message1=hello&message2=world,模型绑定过程从查询字符串中定位操作方法定义的参数值。然后操作过滤器会修改其中一个值。(2)使用特性基类实现操作过滤器
  操作属性也可以通过派生 ActionFiterAttribute 类来实现,该类扩展了 Atribute 并继承了IActionFilter 和 IAsyncActionFilter 接口,以便实现类只覆盖它们需要的方法。

public class ChangeArgAttribute : ActionFilterAttribute
{
    public override async Task OnActionExecutionAsync(ActionExecutingContext context,
            ActionExecutionDelegate next)
    {
        if (context.ActionArguments.ContainsKey("message1"))
        {
            context.ActionArguments["message1"] = "New message";
        }
        await next();
    }
}

(3)使用控制器过滤方法
  Controller 类是呈现 Razor 视图的控制器的基础,它实现了 IActionFilter 和 IAsyncActionFilter 接口,这意味着可以定义功能,并将其应用到由控制器和任何派生控制器定义的操作中。在 Controlers 文件夹的 Homecontoler.cs 中添加如下方法实现 ChangeArg 过滤器功能。

public override void OnActionExecuting(ActionExecutingContext context)
{
    if (context.ActionArguments.ContainsKey("message1"))
    {
        context.ActionArguments["message1"] = "New message";
    }
}

4.4 页面过滤器

  页面过滤器是 Razor Pages 等效的操作过滤器。下面是 IPageFilter 接口,它是由同步页面器实现的:

public interface IPageFilter : IFilterMetadata
{
    void OnPageHandlerExecuted(PageHandlerExecutedContext context);
    void OnPageHandlerExecuting(PageHandlerExecutingContext context);
    void OnPageHandlerSelected(PageHandlerSelectedContext context);
}

  在 ASP.NET Core 选择了页面处理程序方法后,但在执行模型绑定之前,调用 OnPageHandlerSelected 方法,这意味着处理程序方法的参数还没有确定。
  OnPageHandlerExecuting 方法在模型绑定过程完成之后,但在调用页面处理程序方法之前调用。
  OnPageHandlerExecuted 方法在调用页面处理程序方法之后,但在处理操作结果以创建响应之前调用。

  异步页面过滤器是通过实现 IAsyncPageFilter 接口创建的,它的定义如下。

public interface IAsyncPageFilter : IFilterMetadata
{
    Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, 
                                     PageHandlerExecutionDelegate next);
    Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context);
}

  OnPageHandlerExecutionAsync 在选中处理程序方法之后调用,它等价于同步的 OnPageHandlerExecuted 方法。为 OnPageHandlerExecutionAsync 提供了一个允许它短路管道的 PageHandlerExecutingContext 对象,以及一个被调用来传递请求的委托。委托产生一个PageHandlerExecutedContext 对象,该对象可用于检査或更改处理程序方法产生的操作结果。(1)创建页面过滤器
  创建页面过滤器,将一个名为 ChangePageArgs.cs 的类文件添加到 Filters 文件夹。

public class ChangePageArgs : Attribute, IPageFilter
{
    public void OnPageHandlerSelected(PageHandlerSelectedContext context)
    {
        // do nothing
    }
    public void OnPageHandlerExecuting(PageHandlerExecutingContext context)
    {
        if (context.HandlerArguments.ContainsKey("message1"))
        {
            context.HandlerArguments["message1"] = "New message";
        }
    }
    public void OnPageHandlerExecuted(PageHandlerExecutedContext context)
    {
        // do nothing
    }
}

  修改 Message.cshtml Razor Pages 以定义处理程序方法,并应用了页面过滤器。页面过滤器可以应用于单个处理程序方法,也可以应用于页面模型类。

@functions
{
    //[SimpleCache]
    [ChangePageArgs]
    public class MessageModel : PageModel
    {
        public object Message { get; set; } =
            $"{DateTime.Now.ToLongTimeString()}:This is the Message Razor Page";

        public void OnGet(string message1, string message2)
        {
            Message = $"{message1}, {message2}";
        }
    }
}

  请求https://localhost:44350/home/messages?message1=hello&message2=world,页面过滤器替换 OnGet 处理程序方法的 message1 参数值。(2)使用页面模型过滤方法
  PageModel 类用作页面模型类的基础,它实现了 IPageFilter 和 IAsyncPageFilter 接口,这意味着可以直接向页面模型添加过滤功能。在 Pages 文件夹的 Message.cshtml 文件中,注释掉特性,使用 PageModel 过滤方法。

public override void OnPageHandlerExecuting(PageHandlerExecutingContext context)
{
    if (context.HandlerArguments.ContainsKey("message1"))
    {
        context.HandlerArguments["message1"] = "New message";
    }
}

4.5 结果过滤器

  结果过滤器在操作结果用于生成响应之前和之后执行,允许在端点处理响应之后修改响应。下面是IResultFilter 接口的定义:

public interface IResultFilter : IFilterMetadata
{
    void OnResultExecuted(ResultExecutedContext context);
    void OnResultExecuting(ResultExecutingContext context);
}

  在端点生成操作结果后调用 OnResultExecuting 方法。0nResultExecuted 方法在操作结果执行后调用,为客户端生成响应。
  异步结果过滤器实现 IAsyncResultFilter 接口。

public interface IAsyncResultFilter : IFilterMetadata
{
    Task OnResultExecutionAsync(ResultExecutingContext context,
                                ResultExecutionDelegate next);
}

(1)理解始终运行的结果过滤器
  实现 IResultFilter 和 IAsyncResultFilter 接口的过滤器仅在请求由端点正常处理时使用。如果另一个过滤器使管道短路或出现异常,则不使用它们。即使在管道短路时,需要检查或更改响应的过滤器,可以实现 IAlwaysRunResultFiter 或 IAsycAlwaysRunResultFilter 接口。这些接口诞生自 IResultFilter 和 IAsyncResultFilter,但没有定义新的特性。相反,ASP.NET Core 检测始终运行的接口,并始终应用该过滤器。(2)创建结果过滤器
  向 Filters 文件夹添加一个名为 ResultDiagnosticsAttribute.cs 的类文件。

public class ResultDiagnosticsAttribute : Attribute, IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(
        ResultExecutingContext context, ResultExecutionDelegate next)
    {
        if (context.HttpContext.Request.Query.ContainsKey("diag"))
        {
            Dictionary<string, string> diagData =
                new Dictionary<string, string>
                {
                    {"Result type", context.Result.GetType().Name }
                };
            if (context.Result is ViewResult vr)
            {
                diagData["View Name"] = vr.ViewName;
                diagData["Model Type"] = vr.ViewData.Model.GetType().Name;
                diagData["Model Data"] = vr.ViewData.Model.ToString();
            }
            else if (context.Result is PageResult pr)
            {
                diagData["Model Type"] = pr.Model.GetType().Name;
                diagData["Model Data"] = pr.ViewData.Model.ToString();
            }
            context.Result = new ViewResult()
            {
                ViewName = "/Views/Shared/Message.cshtml",
                ViewData = new ViewDataDictionary(
                    new EmptyModelMetadataProvider(),
                    new ModelStateDictionary())
                {
                    Model = diagData
                }
            };
        }
        await next();
    }
}

  此过滤器检查请求是否包含名为 diag 的查询字符串参数,如果包含,则创建一个显示诊断信息的结果,而不是端点生成的输出。
在 Controllers 文件夹的 HomeController.cs 文件中应用结果过滤器。

[ResultDiagnostics]
public class HomeController : Controller

  并请求 https://localhost:44350/?diag,过滤器将检测査询字符串参数。(3)使用属性基类实现结果过滤器
  ResultFilterAttribute 类派生自 Attribute 并实现 IResultFilter 和IAsyncResultFilter 接口,可以作结果过滤器的基类。

public class ResultDiagnosticsAttribute : ResultFilterAttribute
{
    public override async Task OnResultExecutionAsync(
        ResultExecutingContext context, ResultExecutionDelegate next) 
    {
        ......
    }
}

4.6 异常过滤器

  使用异常过滤器,不必在每个操作方法中编写 ty...catch 块,即可响应异常。异常过滤器可以应用于控制器类、操作方法、页面模型类或处理程序方法。当端点或已应用于端点的操作、页面和结果过滤器不处理异常时,将调用它们。异常过滤器实现 IExceptionFilter 接口,其定义如下:

public interface IExceptionFilter : IFilterMetadata
{
    void OnException(ExceptionContext context);
}

  如果遇到未处理的异常,则调用 OnException方法。IAsyncExceptionrilter 接口可以用来创建异步异常过滤器。下面是异步接口的定义:

public interface IAsyncExceptionFilter : IFilterMetadata
{
    Task OnExceptionAsync(ExceptionContext context);
}

  OnExceptionAsync 方法是 xceptionFilter 接口中的 OnException 方法的异步对等物,当有未处理的异常时调用。(1)创建异常过滤器
  异常过滤器可以通过实现一个过滤器接口或从 ExceptionFilterAttribute 类派生来创建,该类派生自 Attribute 并实现了 IExceptionFilter 和 IAsyncExceptionFilter。异常过滤器最常见的用途是为特定异常类型显示自定义错误页面,以便为用户提供比标准错误处理功能更有用的信息。
  要创建异常过滤器,将一个名为 RangeExceptionAttribute.cs 的类文件添加到 Filters 文件夹。

public class RangeExceptionAttribute : ExceptionFilterAttribute
{
    public override void OnException(ExceptionContext context)
    {
        if (context.Exception is ArgumentOutOfRangeException)
        {
            context.Result = new ViewResult()
            {
                ViewName = "/Views/Shared/Message.cshtml",
                ViewData = new ViewDataDictionary(
                    new EmptyModelMetadataProvider(),
                    new ModelStateDictionary())
                {
                    Model = @"The data received by the
                            application cannot be processed"
                }
            };
        }
    }
}

  此过滤器使用 ExceptionContext 对象来获取未处理异常的类型,如果类型是ArgumentOutOfRangeException,则创建一个操作结果,向用户显示一条消息。在 Controllers 文件夹的 HomeController.cs 文件中应用异常过滤器,将一个操作方法添加到主控制器中,对其应用异常过滤器。

[RangeException]
public ViewResult GenerateException(int? id)
{
    if (id == null)
    {
        throw new ArgumentNullException(nameof(id));
    }
    else if (id > 10)
    {
        throw new ArgumentOutOfRangeException(nameof(id));
    }
    else
    {
        return View("Message", $"The value is {id}");
    }
}

  GenerateException 操作方法依赖于默认的路由模式,从请求 URL接收一个可空的 int 值。如果没有匹配的 URL 段,操作方法抛出一个 ArgumentNullException;如果 int 的值大于 10,则抛出一个 ArgumentOutOfRangeException。如果有一个值并且处于范围内,那么操作方法返回 ViewResult。
  并请求https:/localhost:44350/home/generateException/100。最后一个段超过 action 方法所期望的范围,该方法将抛出过滤器所处理的异常类型。如果请求 /home/GenerateException,那么过滤器不会处理操作方法抛出的异常,而使用默认的错误处理。

5 管理过滤器生命周期

  默认情况下,ASP.NET Core 管理它创建的过滤器对象,并在后续请求中重用它们。
  要创建一个显示生命周期的过滤器,将一个名为 GuidResponseAttribute.cs 的类文件添加到 Filters 文件夹中,用它来定义过滤器。

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
    AllowMultiple = true)]
public class GuidResponseAttribute : Attribute, IAsyncAlwaysRunResultFilter
{
    private int counter = 0;
    private string guid = Guid.NewGuid().ToString();
    public async Task OnResultExecutionAsync(ResultExecutingContext context,
        ResultExecutionDelegate next)
    {
        Dictionary<string, string> resultData;
        if (context.Result is ViewResult vr
            && vr.ViewData.Model is Dictionary<string, string> data)
        {
            resultData = data;
        }
        else
        {
            resultData = new Dictionary<string, string>();
            context.Result = new ViewResult()
            {
                ViewName = "/Views/Shared/Message.cshtml",
                ViewData = new ViewDataDictionary(
                    new EmptyModelMetadataProvider(),
                    new ModelStateDictionary())
                {
                    Model = resultData
                }
            };
        }
        while (resultData.ContainsKey($"Counter_{counter}"))
        {
            counter++;
        }
        resultData[$"Counter_{counter}"] = guid;
        await next();
    }
}

  此结果过滤器将端点产生的操作结果替换为呈现消息视图并显示唯一 GUID 值的操作结果,对过滤器进行了配置,以便它可以多次应用于相同的目标,并且如果管道中较早的过滤器创建了合适的结果,它将添加新消息。
  如下在 Controllers 文件夹的 HomeControler.cs 文件中,对主控制器应用了两次过滤器(为了简活美郇还删除了所有操作方法,只保留了一个)。

[HttpsOnly]
[ResultDiagnostics]
[GuidResponse]
[GuidResponse]
public class HomeController : Controller
{
    public async Task<IActionResult> Index()
    {
        return View("Message", "这是home控制器的Index方法");
    }
}

  要确认过滤器的重用,请重新启动 ASP.NET Core 并请求 https://localhost:44350/?diag。响应包含来自两个 GuidResponse 过滤器属性的 GUID 值。创建过滤器的两个实例来处理请求。重新加载浏览器,将看到显示的相同 GUID 值,这表明为处理第一个请求而创建的过滤器对象已重用。

5.1 创建过滤器工厂

  过滤器可以实现 IFilterFactory 接口来负责创建过滤器的实例,并指定是否可以重用这些实例。在 Filters 文件夹的 GuidResponseAtribute.cs 文件中实现接口,并为 IsReusable 属性返回 false,这将阻止过滤器的重用。

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
    AllowMultiple = true)]
public class GuidResponseAttribute : Attribute, IAsyncAlwaysRunResultFilter,
    IFilterFactory
{
    private int counter = 0;
    private string guid = Guid.NewGuid().ToString();
    public bool IsReusable => false;
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return ActivatorUtilities
            .GetServiceOrCreateInstance<GuidResponseAttribute>(serviceProvider);
    }
    ......

  使用 GetServiceOrCreateInstance 方法创建新的过滤器对象,该方法由 Microsof.Extensices.DependencyInjecton 名称空间中的 ActivatorUtilities 类定义。尽管可以使用 new关键字创建过滤器,但此方法解析通过过滤器的构造函数声明的、对服务的任何依赖关系。
  要查看实现 IFiterFactory 接口的效果,请重新启动并请求https://localhost:44350/?diag。每次处理请求时,创建新的过滤器,并显示新的 GUID。

5.2 依赖注入过滤器的生命周期

  过滤器可以注册为服务,这允许通过依赖注入来控制它们的生命周期。 在 Startup.cs 文件中创建过滤器服务。

services.AddScoped<GuidResponseAttribute>();

6 创建全局过滤器

  全局过滤器应用于 ASPNET Core 处理的每一个请求,这意味着它们不必应用于单个控制器或Razor Pages。任何过滤器都可以用作全局过滤器;然而,操作过滤器将仅应用于端点是操作方法的请求,而页面过滤器将仅应用于端点是 Razor Pages 的请求。
  使用 Startup 类中的 options 模式设置全局过滤器,如下所示。

services.Configure<MvcOptions>(opts => opts.Filters.Add<HttpsOnlyAttribute>());

  MvcOptions.Filters 属性返回一个集合,其中添加了过滤器以全局应用它们,对于同样是服务的过滤器,可以使用 Add<T>方法或使用 AddService<T>方法。还有一个没有泛型类型参数的 Add 方法,可用于将特定对象注册为全局过滤器。

7 理解和改变过滤器的顺序

  过滤器以特定顺序运行:授权、资源、操作(或页面)和结果。但是,如果有给定类型的多个过滤器,则应用它们的顺序由应用过滤器的范围驱动。
为了演示它是如何工作的,在 Filters 文件夹中添加一个名为 MessageAttibute.cs 的类文件。

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
    AllowMultiple = true)]
public class MessageAttribute : Attribute, IAsyncAlwaysRunResultFilter
{
    private int counter = 0;
    private string msg;
    public MessageAttribute(string message) => msg = message;
    public async Task OnResultExecutionAsync(ResultExecutingContext context,
           ResultExecutionDelegate next)
    {
        Dictionary<string, string> resultData;
        if (context.Result is ViewResult vr
            && vr.ViewData.Model is Dictionary<string, string> data)
        {
            resultData = data;
        }
        else
        {
            resultData = new Dictionary<string, string>();
            context.Result = new ViewResult()
            {
                ViewName = "/Views/Shared/Message.cshtml",
                ViewData = new ViewDataDictionary(
                    new EmptyModelMetadataProvider(),
                    new ModelStateDictionary())
                {
                    Model = resultData
                }
            };
        }
        while (resultData.ContainsKey($"Message_{counter}"))
        {
            counter++;
        }
        resultData[$"Message_{counter}"] = msg;
        await next();
    }
}

  此结果过滤器使用前面示例中展示的技术来替换来自端点的结果,并允许多个过滤器构建一系列显示给用户的消息。在 Controllers 文件夹的 HomeController.cs 文件中将消息过滤器的几个实例应用于主控制器。

[Message("This is the controller-scoped filter")]
public class HomeController : Controller
{
    [Message("This is the first action-scoped filter")]
    [Message("This is the second action-scoped filter")]
    public async Task<IActionResult> Index()
    {
        return View("Message", "这是home控制器的Index方法");
    }
}

  全局注册了消息过滤器。

services.Configure<MvcOptions>(opts =>
{
    opts.Filters.Add<HttpsOnlyAttribute>();
    opts.Filters.Add(new MessageAttribute("This is the globally-scoped filter"));
});

  同一个过滤器有四个实例。要查看应用它们的顺序,请求https://localhost:44350。默认情况下,ASP.NET Core 运行全局过滤器,然后过滤器应用于控制器或页面模型类,最后过滤器应用于操作或处理程序方法。改变过滤器顺序
  默认的顺序可以通过实现 IOrderedFilter 接口来改变。Order 属性返回一个 int 值,低值的过滤器会在高 Order 值的过滤器之前应用。

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
    AllowMultiple = true)]
public class MessageAttribute : Attribute, IAsyncAlwaysRunResultFilter,
        IOrderedFilter
{
    private int counter = 0;
    private string msg;
    public MessageAttribute(string message) => msg = message;
    public int Order { get; set; }
    ......
}
[Message("This is the controller-scoped filter", Order = 10)]
public class HomeController : Controller
{
    [Message("This is the first action-scoped filter", Order = 1)]
    [Message("This is the second action-scoped filter", Order = -1)]
    public async Task<IActionResult> Index()
    {
        return View("Message", "这是home控制器的Index方法");
    }
}
[Core]Asp.Net Core中的各种过滤器(授权、资源、操作、结果、异常) 说明 提到过滤器,通常是指请求处理管道中特定阶段之前或之后的代码,可以处理:授权、响应缓存(对请求管道进行短路,以便返回缓存的响应)、 防盗链、本地化国际化等,过滤器用于横向处理业务,符合Aop思想,它也可以有效的避免代码的重复复制。 在Asp.Net Core中,有5种过滤器,分别是授权、资源、操作、结果、异常五大过滤器,与之前的Asp.Net 相比,多了一个资源过滤器,剩下的4个授权、 操作、结果、异常过滤器则没有什么太大的区别。 PS: 传统Asp.Net 中的4种过滤器参考 https://w. 阅读详情

相关推荐

2021-06-13 .NET高级班 53-ASP.NET Core中Filter的过滤器使用(ExceptionFilter,ActionFilter,ResourceFilter)

ExceptionFilter public class CustomExceptionFilterAttribute : ExceptionFilterAttribute { #region Identity private readonly ILogger<CustomExceptionFilterAttribute> _logger; private readonly IModelMetadataProvider _modelMeta

愚公智库 51万+

ASP.NET Core 中的五中过滤器

再来回顾下上面Action 注册方式或者Controller 注册方式 也即Attribute 特性标注注册方式,本身基础的特性是不支持构造函数的,是在运行时注册进来的,那要解决这种本身需要对服务依赖的过滤器需要使用 TypeFilter 或者ServiceFilter 方式进行过滤器的标注注册。现在有些同学考虑了一些全局的情况,比如我要全局处理系统中的异常,或者收集操作日志等,需要全局注册一个ExceptionFilter 来实现,就不需要每一个Controller 中进行代码注册,方便快捷。

和光同尘的专栏 952

Ubuntu 24.04 LTS与Windows 11远程桌面连接:从xrdp闪退到xfce4桌面环境的实战排障

本文详细解决了在Ubuntu 24.04 LTS上使用xrdp连接Windows 11远程桌面时遇到的登录后闪退问题。通过分析日志,定位到默认GNOME桌面环境与xrdp的兼容性冲突,并提供了将桌面环境更换为轻量且兼容性更佳的XFCE4的完整实战解决方案,包括安装、关键配置修改及服务重启步骤,确保远程连接稳定可靠。

mango的博客 805

ASP.NET Core 中的五种过滤器

ASP.NET Core 中的五种过滤器 IActionFilter IAuthorizationFilter IExceptionFilter IResourceFilter IResultFilter

长铗归来的博客 2348

RL调参侠之BipedalWalker BipedalWalkerHardcore SAC

前几天用PPO训练了个很一般的agent: RL调参侠之BipedalWalker PPO 我看SpinningUp上面说PPO和SAC是两个领域里各自的State_of_the_art的方法,所以就打算跑SAC试试看,之前因为Open AI Five的原因,一直感觉PPO很牛掰,实际上现在很多自动驾驶的算法也是用PPO在跑,毕竟有一个“必定能够提升期望”的理论保证在这里,我理解这对做项目很大的诱惑。 不过我跑出来的结果明显是SAC更好。 调参侠时间 策略熵的权重alpha用的0.2,我一开始没有看SAC的

weixin_48370148的博客 2099

避开这5个坑!ADNI数据库下载实操经验分享(含断点续传方案)

本文分享了ADNI数据库下载的5个常见陷阱及解决方案,包括高级搜索策略优化、断点续传技巧和数据完整性验证。特别针对大文件下载中断问题,提供了wget断点续传和分卷下载的实操方法,帮助研究者高效获取神经影像数据,避免重复下载和时间浪费。

git9versioner的博客 373

详解Asp.Net Core管道模型中的五种过滤器的适用场景与用法

在 ASP.NET Core 中,过滤器是一种用于对请求管道进行前置或后置处理的组件。它们可以在请求处理的不同阶段干预和修改请求和响应,以实现一些通用的处理逻辑或功能增强。ASP.NET Core 的管道模型由多个中间件组成,而过滤器是这个模型中的一部分。它们可以在中间件之前或之后执行,并且可以应用于整个应用程序、控制器、动作方法或特定的路由。过滤器可以用于多种场景,例如添加身份验证、授权、缓存、异常处理等。本文我将介绍 .NET Core 中常用的几种 过滤器。

dotnet研习社 1852

测试人必看!正交表与Allpairs工具对比:如何选择最适合你的组合测试方案?

本文深入对比了正交表、PICT与Allpairs三种主流组合测试方案,帮助测试人员根据项目场景做出最佳选择。文章分析了各自的核心原理、优劣势及适用场景,特别强调了在涉及复杂约束时PICT工具的强大功能,并提供了从原理学习到CI/CD集成的实战选型指南。

weixin_29224309的博客 267

第十五节:Asp.Net Core中的各种过滤器(授权、资源、操作、结果、异常)

一. 简介 1. 说明   提到过滤器,通常是指请求处理管道中特定阶段之前或之后的代码,可以处理:授权、响应缓存(对请求管道进行短路,以便返回缓存的响应)、防盗链、本地化国际化等,过滤器用于横向处理业务,符合Aop思想,它也可以有效的避免代码的重复复制。   在Asp.Net Core中,有5种过滤器,分别是授权、资源、操作、结果、异常五大过滤器,与之前的Asp.Net 相比,...

weixin_30553065的博客 6573

决策大模型与生成式大模型有何不同?一文拆解技术框架与落地路径

大模型技术正从内容生成走向复杂场景的智能决策。与生成式大模型拟合概率分布不同,决策大模型以序列决策为核心,融合强化学习机制,在动态环境中感知反馈、持续修正行动路径。其技术框架涵盖语义理解、规划推理与闭环决策,并通过世界模型进行低成本仿真推演,显著提升供应链调度、物流排程等场景的应对效率。实际落地时需结合传统优化算法与人工兜底,构建“语义先导+数值寻优”的分层架构,才能平衡精度与稳定性。这篇内容拆解决策大模型的技术组成、落地路径与常见误区,为企业智能化转型提供可参考的实践框架。

weixin_29057163的博客 187

IMU标定中的Allen方差

Allen方差 Allen方差是IMU标定任务中的常用工具。IMU标定可以分为确定性误差和随机误差,确定性误差包括:scale、bias、misalignment。随机误差则主要是高斯白噪声和bias随机游走。Allen方差主要用于标定随机误差。 Allen方差获取 Allen方差的计算方法如下: 保持IMU静止, 采集N个数据点。 将N个点定长分组,常用的交叠式Allen方差如下(n=3): 对于每个子集,计算均值后,计算相邻两组的方差,得到的时间-方差曲线即为Allen方差。 Alle

realjc的博客 5733

asp.net core

用于创建一个 Web 应用程序的构建器 (WebApplicationBuilder) 实例。通过这个构建器,你可以配置应用程序的服务和中间件,进而构建和运行你的 ASP.NET Core Web 应用。 用于设置Web服务器所监听的URL和端口。通过这个方法,开发者可以指定应用程序在哪些地址和端口上运行,从而控制外部请求如何到达你的应用程序。 允许你访问应用程序的配置设置,这些设置通常包含在 appsettings.json、环境变量、用户机密、命令行参数等中。builder.Configuration

备忘 1398

YOLOv11改进策略【Neck】| BiFPN:双向特征金字塔网络-跨尺度连接和加权特征融合

可扩展的高效物体检测BiFPN(加权双向特征金字塔网络)

Limiiiing的博客 5493

ASP.NET Core3 现代 Web 开发(一)

在本书中,我们将讨论 Microsoft 添加到 ASP.NET Core 3 中的新功能和最新功能。我们将深入研究这些应用,并了解如何将它们应用于已引入的各种新工具。我们将关注 Blazor、gRPC、dotnet工具、错误处理方法和 Razor 页面。这次我们有这么多新的话题要看,所以这将是一个地狱般的欢乐之旅。坐下来享受吧!在第一章中,我们介绍了 ASP.NET Core 和.NET Core 中一些最大的变化。

龙哥盟 1245

基于ESP32 蓝牙游戏手柄设计

介绍使用 ESP32 并通过 BLE 通信的 设计手持游戏手柄,包含原理图与代码

DT程序员的博客 2163

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

ASP.NET Core如何在ActionFilterAttribute里做依赖注入

文章来源:http://www.dalbll.com/group/topic/asp.net/6333 在ASP.NET Core里,我们可以使用构造函数注入很方便地对Controller,ViewComponent等部件做依赖注入。但是如何给过滤器ActionFilterAttribute也用上构造函数注入呢? 问题我的博客系统里有个用来删除订阅文件缓存的ActionFilter,...

dizhu1708的博客 1434

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

ASP.NET Core 中文教程

ASP.NET Core 中文教程 ASP.NET Core 学习 目录 介绍 第一章 入门 第二章 指南 1用 Visual Studio Code 在 macOS 上创建首个 ASP.NET Core 应用程序 2用 Visual Stud...

chounie7016的博客 414

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

.NET Core笔试题

文章目录.NET Core笔试题1.如何在ASP.NET Core中激活Session功能?2.什么是中间件?3.Applicationbuilder的Use和Run方法有什么区别?4.如何使taghelper在元素这一层上失效?5.什么是ASP.NET Core?6.ASP.NET Core 中AOP的支持有哪些?7.ASP.NET Core Filter的注册方式有哪些?8.ASP.NET Core Filter如何支持依赖注入?9.ASP.NET Core 如何和读取配置文件中的内容?10.ASP.N

乔巴`的博客 1549

ASP .NET Core笔试题总结-献给即将找工作的同行们

1.如何在ASP.NET Core中激活Session功能? 首先要添加session包. 其次要在configservice方法里面添加session。然后又在configure方法里面调用usesession。。 2.什么是中间件? 中间件在这里是指注入到应用中处理请求和响应的组件。是通过多个委托来嵌套形成的一个俄罗斯套娃! 3.Applicationbuilder的Use和Run方法有什么区别? 这两个方法都在startup 类的configure方法里面调用。都是用来向应用请求管道里面添.

飘零落叶 619

Asp.Net Core遇到Swagger(四)-Swashbuckle技巧c篇

接Swashbuckle技巧c篇,与Action相关的配置和操作,此处为c篇内容。

关关长语 3065

开发Asp.Net Core 学习笔记(五)-- Filter

原文地址:https://www.cnblogs.com/niklai/p/5676632.html 第一部分、MVC框架内置过滤器 下图展示了Asp.Net Core MVC框架默认实现的过滤器的执行顺序: Authorization Filters:身份验证过滤器,处在整个过滤器通道的最顶层。对应的类型为: AuthorizeAttribute.cs  Resourc...

awei0130的博客 643

ASP.NET CORE 学习之自定义异常处理

为什么异常处理选择中间件? 传统的ASP.NET可以采用异常过滤器的方式处理异常,在ASP.NET CORE中,是以多个中间件连接而成的管道形式处理请求的,不过常用的五大过滤器得以保留,同样可以采用异常过滤器处理异常,但是异常过滤器不能处理MVC中间件以外的异常,为了全局统一考虑,采用中间件处理异常更为合适 为什么选择自定义异常中间件? 先来看看ASP.NET CORE 内置的三个异...

weixin_30800807的博客 340

.Net Core过滤器:2.资源过滤器;3.action过滤器;4.异常处理过滤器

除了身份认证(授权)过滤器之外,还有三个过滤器: 2.资源过滤器;3.action过滤器;4.异常处理过滤器 都是大同小异的 资源过滤器: /// <summary> /// 资源过滤器(授权过滤器之后执行) /// </summary> public class ResourceFilterAttribute : Attribute...

Ling、bug 666

ASP.NET Core 中文文档目录

ASP.NET Core 中文文档目录 翻译计划 五月中旬 .NET Core RC2 如期发布,我们遂决定翻译 ASP.NET Core 文档。我们在 何镇汐先生、 悲梦先生、 张仁建先生和 雷欧纳德先生的群中发布了翻译计划招募信息,并召集到一群小伙伴。我们从六月开始翻译文档,目前已完成前两章,并将按计划以连载的方式逐篇公布。当翻译计划完成后,所有文档将整合到一...

195
上一篇: ASP.NET Core应用程序12:使用模型验证
下一篇: ASP.NET Core应用程序14:创建表单应用程序
似水明俊德
博客等级 码龄8年 247粉丝 276原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值