重庆熊猫 Loading

ASP.NET Core教程-Filter(过滤器)

更新记录
转载请注明出处:
2022年11月24日 发布。
2022年11月20日 从笔记迁移到博客。

过滤器(Filter)基础

过滤器说明

过滤器与中间件很相似,过滤器(Filters)可在管道(pipeline)特定阶段(particular stage)前后执行操作。可以将过滤器视为拦截器(interceptors)。

过滤器级别范围(Scope of filters)

过滤器有多个级别,分别是:

  • 全局级别过滤器(Global scope

    This means that the filter covers the whole MVC pipeline. Every call to a specific MVC route will pass through that filter.

  • 控制器级别过滤器(Controller scope

    In this case, the filter is initialized as an attribute in one or multiple controller classes. It will act only on requests that have been directed to the target controllers.

  • 动作级别过滤器(Action scope

    The filter is initialized as an attribute in one or multiple action methods. It will act only on requests that have been directed to the target methods.

预定义过滤器的类型(Out-of-the-box Filter Types)

There are different kinds of filters, and each filter is executed at a different stage in the pipeline. For example, action filters are executed when the action method is executed

Filter type Type description
Authorization(授权) This kind of filter is related to the authorization of users. It is the first filter that's executed in the filter pipeline and can short-circuit the pipeline of requests.
Resource(资源) Resource filters run immediately after authorization filters and after the rest of the pipeline has completed. They're useful when we wish to implement caching or for performance implementations.
Page(页面) 这种类型的过滤器用于在 Razor Pages 处理程序方法接收到请求之前修改请求,或者在生成操作结果之后修改操作结果。这种类型的过滤器只能应用于Razor Pages
Action(操作) Action filters are focused on the life cycle of action methods. They intercept and change the arguments and the returning results of action methods.仅在MVC中起作用。
Exception(异常) Exception filters are used to intercept and apply cross-cutting policies to unhandled exceptions.
Result(结果) Result filters are executed immediately before and after the execution of an action result. They are usually implemented to change the formatting of the outcome. It is essential to note that they are executed only when the action method is completed successfully.

不同类型的过滤器在ASP.NET Core中的位置。可以看到不同类型的过滤器在不同阶段起作用。授权过滤器先于其他所有操作,并在任何授权错误时阻止请求。 资源过滤器在模型验证和模型绑定请求之前运行,也在我们的请求结果从服务器返回时运行。 动作过滤器类型在动作调用之前和之后起作用。 此外,如果操作引发异常,则会触发异常过滤器。 在管道的末尾,结果过滤器对 IActionResult 最终对象实例进行操作。

image

image

Authorization filters
最先执行,用于判断用户是否授权。如果未授权,则直接结束当前请求。这种类型的过滤器实现了 IAsyncAuthorizationFilter 或IAuthorizationFilter 接口。

Resource filters
在Authorization过滤器后执行,并在执行其他过滤器 (除Authorization过滤器外)之前和之后执行。由于它在Action之前执行,因而可以用来对请求判断,根据条件来决定是否继续执行Action。这种类型过滤器实现了 IAsyncResourceFilter 或 IResourceFilter 接口。These are the filters that handle the request after authorization and are the last ones to handle the request before it leaves the filter pipeline. They are used to implement caching or by passing the filter pipeline。

Action filters
在Action执行的前后执行。与Resource过滤器不一样,它在模型绑定后执行。这种类型的过滤器实现了 IAsyncActionFilter 或 IActionFilter 接口。These wrap calls to individual action method calls and can manipulate the arguments passed in the action as well as the action result returned from it。

Exception filters
异常过滤器用于管理未处理的异常,比如:用于捕获异常。这种类型的过滤器实现了 IAsyncExceptionFilter 或 IExceptionFilter 接口。

Result filters
在 IActionResult 执行的前后执行,使用它能够控制Action的执行结果,比如:格式化结果等。需要注意的是,它只有在Action方法成功执行完成后才会运行。这种类型过滤器实现了 IAsyncResultFilter 或 IResultFilter 接口。

过滤器预定义接口

过滤器接口都实现了IFilterMetadata接口,它在Microsoft.AspNetCore.Mvc.Filters名称空间中。该接口是空的,并且不需要过滤器来实现任何特定行为。因为每个过滤器类别都以不同的方式工作。

namespace Microsoft.AspNetCore.Mvc.Filters
{
    public interface IFilterMetadata { }
}

预定义的接口:

过滤器类型 接口 对应特性
授权过滤器 IAuthorizationFilter、IAsyncAuthorizationFilter 没有提供特性类
资源过滤器 IResourceFilter、IAsyncResourceFilter 没有提供特性类
操作过滤器 IActionFilter、IAsyncActionFilter ActionFilterAttribute
页面过滤器 IPageFilter、IAsyncPageFilter 没有提供特性类
结果过滤器 IResultFilter、IAsyncResultFilter、
IAlwaysRunResultFilter、IAsyncAlwaysRunResultFilter
ResultFilterAttribute
异常过滤器 IExceptionFilter、IAsyncExceptionFilter ExceptionFilterAttribute

授权过滤器(Authorization Filter)

说明

授权过滤器用于实现应用程序的安全策略。授权过滤器在其他类型的过滤器和端点处理请求之前执行,可以看前面的图片。

用IAuthorizationFilter接口实现授权过滤,定义如下:

namespace Microsoft.AspNetCore.Mvc.Filters {
	public interface IAuthorizationFilter : IFilterMetadata
    {
        //调用 OnAuthorization 方法,实现授权过滤
        void OnAuthorization(AuthorizationFilterContext context);
    }
}

对应的异步版本,IAsyncAuthorizationFilter接口,定义如下:

using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Mvc.Filters {
	public interface IAsyncAuthorizationFilter : IFilterMetadata {
         //调用 OnAuthorizationAsync方法,以便过滤器可以对请求进行授权
		Task OnAuthorizationAsync(AuthorizationFilterContext context);
    }
}

两个接口都通过AuthorizationFilterContext参数进行对授权结果进行处理。通过这个参数对象的result属性可以对授权结果进行处理。

自定义授权过滤器

定义一个类继承IAuthorizationFilter接口或其异步版本接口即可。一般情况下会同时定义同步版本和异步版本。

新建一个Filters文件夹,在文件夹下建立PandaAsyncAuthorizationFilter.cs和PandaAuthorizationFilter.cs文件,分别存放同步版本和异步版本的自定义授权过滤器。

PandaAuthorizationFilter.cs

using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace test.Filters
{
    /// <summary>
    /// 自定义熊猫授权过滤器
    /// </summary>
    public class PandaAuthorizationFilter : Attribute,IAuthorizationFilter
    {
        /// <summary>
        /// 进行认证
        /// </summary>
        /// <param name="context"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            //如果不是Https直接给我返回未授权
            if(!context.HttpContext.Request.IsHttps)
            {
                context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized); 
            }
        }
    }
}

PandaAsyncAuthorizationFilter.cs

using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace test.Filters
{
    /// <summary>
    /// 自定义熊猫授权过滤器(异步版本)
    /// </summary>
    public class PandaAsyncAuthorizationFilter : Attribute, IAsyncAuthorizationFilter
    {
        public Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            //如果不是Https直接给我返回未授权
            if (!context.HttpContext.Request.IsHttps)
            {
                context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized);
            }

            return Task.CompletedTask;
        }
    }
}

在控制器中使用

[PandaAsyncAuthorizationFilter]
[HttpGet("Test")]
public string Test()
{
    return "Test";
}

在上面中,我们定义的是自定义授权过滤器的特性类型。也可以直接定义全局的授权过滤器。比如定义:


资源过滤器(Resource Filter)

说明

资源过滤器在每个请求的前后执行操作。

IResourceFilter接口

namespace Microsoft.AspNetCore.Mvc.Filters {
    public interface IResourceFilter : IFilterMetadata {
        //在授权过滤器之后执行,在模型验证前执行
        void OnResourceExecuting(ResourceExecutingContext context);
        //在执行结果处理之前再生成结果
        void OnResourceExecuted(ResourceExecutedContext context);
    }
}

IAsyncResourceFilter接口

namespace Microsoft.AspNetCore.Mvc.Filters {
    public interface IAsyncResourceFilter : IFilterMetadata {
        Task OnResourceExecutionAsync(ResourceExecutingContext context,ResourceExecutionDelegate next);
    }
}

实例:自定义资源过滤器(同步)

继承自IResourceFilter接口即可。这是一个实现缓存的过滤器特性类。

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
namespace WebApp.Filters {
    //继承Attribute、IResourceFilter
    public class SimpleCacheAttribute : Attribute, IResourceFilter
    {
        //用于存放缓存数据的字典集合
        private Dictionary<PathString, IActionResult> CachedResponses = new Dictionary<PathString, IActionResult>();
        
        //对请求进行处理
        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            //获得请求的Path
            PathString path = context.HttpContext.Request.Path;
            //检测Path是否在缓存集合中
            if (CachedResponses.ContainsKey(path)) {
                //存在缓存,直接将缓存好的结果做返回
                context.Result = CachedResponses[path];
                //然后移除旧的缓存(意味着缓存只用1次)
                CachedResponses.Remove(path);
            }
        }
        
        public void OnResourceExecuted(ResourceExecutedContext context)
        {
            //如果缓存不存在,则将缓存加入到缓存字典集合中
            CachedResponses.Add(context.HttpContext.Request.Path, context.Result);
        }
    }
}

在控制器中使用

[SimpleCache]
public class PandaClass: Controller
{
    
}

实例:自定义资源过滤器(异步)

继承自IAsyncResourceFilter接口即可。这是一个实现缓存的过滤器特性类。

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace WebApp.Filters {
    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];
                //并把旧的缓存删除(意味着缓存只用1次)
                CachedResponses.Remove(path);
            }
            else
            {
                //如果没有命中缓存,则直接进入下一个中间件
                ResourceExecutedContext execContext = await next();
                //等其他中间件执行完成,回到资源过滤器这,将缓存添加到缓存集合中
                CachedResponses.Add(context.HttpContext.Request.Path,execContext.Result);
            }
        }
    }
}

在控制器中使用

[SimpleCache]
public class PandaClass: Controller
{
    
}

动作过滤器(Action Filter)

说明

操作过滤器执行2次,进入1次,退出1次。只能用在Controller和Action上。

IActionFilter接口

namespace Microsoft.AspNetCore.Mvc.Filters
{
    public interface IActionFilter : IFilterMetadata
    {
        //调用方法前执行
        void OnActionExecuting(ActionExecutingContext context);
        //调用方法后执行
        void OnActionExecuted(ActionExecutedContext context);
    }
}

IAsyncActionFilter接口

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

实例:自定义动作过滤器(异步)

继承自IAsyncActionFilter接口即可。下面实现的是查找Action中名为message1的参数,并设置参数的值为message。

using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Threading.Tasks;
namespace WebApp.Filters
{
    public class ChangeArgAttribute : Attribute, IAsyncActionFilter
    {
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            //检测Action是否包含message1参数,如果包含则设置值为message
            if (context.ActionArguments.ContainsKey("message1"))
            {
                context.ActionArguments["message1"] = "message";
            }
            
            await next();
        }
    }
}

实例:自定义动作过滤器(同步)

using Microsoft.AspNetCore.Mvc.Filters;

namespace SampleAPI.Filters
{
        public class CustomActionFilter : IActionFilter
        {
            public void OnActionExecuting(ActionExecutingContext context)
            {
                // do something before the action executes
            }

            public void OnActionExecuted(ActionExecutedContext context)
            {
                // do something after the action executes
            }
        }
}

实例:自定义动作过滤器(异步)

自定义异步动作过滤器就只用一个方法即可。定义类型继承自 IAsyncActionFilter 接口,然后实现 OnActionExecutionAsync 方法。

using System.Threading.Tasks;
//引入过滤器命名空间
using Microsoft.AspNetCore.Mvc.Filters;

namespace SampleAPI.Filters
{
    public class CustomActionFilterAsync : IAsyncActionFilter
    {
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            //Before 
 		   //....
            
            var resultContext = await next();
 
            //After
            //......
        }
    }
}

实例:在预定义的过滤器上自定义过滤器

比如通过继承 ActionFilterAttribute 过滤器,实现一个自定义过滤器。

using Microsoft.AspNetCore.Mvc.Filters;

namespace SampleAPI.Filters
{
        public class CustomControllerFilter : ActionFilterAttribute
        {
            public override void OnActionExecuting(ActionExecutingContext context)
            {
                // do something before the action executes
            }

            public override void OnActionExecuted(ActionExecutedContext context)
            {
                // do something after the action executes
            }
        }
}

使用过滤器

[Route("api/order")]
[CustomControllerFilter]
public class OrderController : ControllerBase
{ 

异常过滤器(Exception Filter)

说明

异常过滤器可以用于处理Controller和Action的异常,如果它们自身没有对异常进行处理,则会让异常过滤器进行处理。

常用于处理特定的异常问题,比如显示异常自定义页面。

IExceptionFilter接口

namespace Microsoft.AspNetCore.Mvc.Filters {
    public interface IExceptionFilter : IFilterMetadata {
        void OnException(ExceptionContext context);
    }
}

IAsyncExceptionFilter接口

using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Mvc.Filters {
    public interface IAsyncExceptionFilter : IFilterMetadata {
        Task OnExceptionAsync(ExceptionContext context);
    }
}

实例:自定义异常过滤器

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System;
namespace WebApp.Filters
{
    public class RangeExceptionAttribute : ExceptionFilterAttribute
    {
        public override void OnException(ExceptionContext context)
        {
            //检测是否ArgumentOutOfRangeException异常
            if (context.Exception is ArgumentOutOfRangeException)
            {
                //如果是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"
                    }
                };
            }
        }
    }
}

在Controller中使用

[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}");
    }
}

结果过滤器(Result Filter)

说明

结果过滤器在操作结果用于生成响应之前和之后执行。

IResultFilter接口

namespace Microsoft.AspNetCore.Mvc.Filters {
    public interface IResultFilter : IFilterMetadata {
        void OnResultExecuting(ResultExecutingContext context);
        void OnResultExecuted(ResultExecutedContext context);
    }
}

IAsyncResultFilter接口

namespace Microsoft.AspNetCore.Mvc.Filters {
    public interface IAsyncResultFilter : IFilterMetadata {
        Task OnResultExecutionAsync(ResultExecutingContext context,
                                    ResultExecutionDelegate next);
    }
}

实例:创建自定义结果过滤器

过滤器通过检查请求是否包含diag的查询字符串的参数,如果包含则创建一个诊短信息的结果。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace WebApp.Filters
{
    public class ResultDiagnosticsAttribute : Attribute, IAsyncResultFilter
    {
        public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
        {
            //检测请求的参数是否包含diag参数
            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();
        }
    }
}

在Controller中使用

[ResultDiagnostics]
public class HomeController : Controller
{
    
}

页面过滤器(Page Filter)

说明

页面过滤器是 Razor Pages 等效的操作过滤器。

IPageFilter接口

namespace Microsoft.AspNetCore.Mvc.Filters {
    public interface IPageFilter : IFilterMetadata {
        void OnPageHandlerSelected(PageHandlerSelectedContext context);
        void OnPageHandlerExecuting(PageHandlerExecutingContext context);
        void OnPageHandlerExecuted(PageHandlerExecutedContext context);
    }
}

全局过滤器( Global Filters)

说明

全局过滤器将会被应用到每个请求。实现过滤器后,然后直接注册到服务中即可。

如何注册全局过滤器

注册到服务中。

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

使用过滤器

使用全局级别的控制器

在MVC服务中直接增加过滤器即可。

builder.Services.AddControllersWithViews(config=>
            config.Filters.Add(new CustomPandaFilter())
);

使用控制器级别的过滤器

[Route("api/order")]
[CustomControllerFilter]    //使用特性修饰即可
public class OrderController : ControllerBase
{

自定义过滤器(Writing a Custom Filter)

说明

当要创建过滤器时,最好分别实现IXXXFilter或IAsyncXXXFilter,这两个接口的区别是前者同步、后者异步。
ASP.NET Core MVC会首先检查异步实现,如果没有实现异步方式,则继续检查同步实现。

实例:只允许在星期日进行用户操作的接口

我们实现一个自定义的动作过滤器来达到这个目标。通过实现IAsyncActionFilter和IActionFilter接口。

在IActionFilter接口中包括两个方法,分别表示Action执行前与执行后要执行的方法。

public interface IActionFilter : IFilterMetadata
{
    //动作执行前操作
    void OnActionExecuted(ActionExecutedContext context);
    //动作执行后操作
    void OnActionExecuting(ActionExecutingContext context);
}

在IAsyncActionFilter接口中,仅有一个OnActionExecutionAsync方法,该方法的第二个参数ActionExecutionDelegate表示要执行的Action。它是一个委托类型,因此在这个方法的内部可以直接调用next(),并在next()前后执行相应的代码。

public interface IAsyncActionFilter : IFilterMetadata
{
    Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next);
}

自定义过滤器的过程

定义过滤器类型

新建继承自Attribute, IActionFilter的类型(这里用Action过滤器举例)
注意要引入命名空间:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

为了能够使用这个新创建的Action过滤器,首先应在ASP.NET Core MVC的Filter集合中添加它。

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>options.Filters.Add<ActionParameterValidationFilter>());
}

注册过滤器到容器

在Startup中注册过滤器会使它影响到应用程序中的每个Action
因此这种做法是全局性的,如果要仅为一个或者少数几个Action添加过滤器,就不能使用这种方式,而要使用特性。
ASP.NET Core MVC对每一种类型的过滤器都定义了相应的特性,如ActionFilterAttribute,它不仅实现了IActionFilter和IAsyncActionFilter两个接口,并且继承自Attribute类。因此,只要使之前创建的自定义过滤器继承自这个特性类即可,如下所示:

public sealed class ActionParameterValidationFilterAttribute: ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        //…
    }
}

此时,只要在需要的Action上面添加该特性即可。

[ActionParameterValidationFilter]
[HttpGet("{keyword}")]
public IActionResult Get(string keyword, int top)
{
}

在自定义过滤器中,有时需要引用其他服务,比如在其中需要引用数据服务来
获取数据,并根据实际情况进行相应的控制。此时,仍然可以使用构造函数注入的
方式,将所依赖的服务注入过滤器中,具体如下

public class ActionParameterValidationFilter: IAsyncActionFilter
{
    public ActionParameterValidationFilter(IDataService dataService)
    {
        DataService = dataService;
    }
    public IDataService DataService { get; }
    // …
}

实例

周日用户才可以操作的过滤器

创建继承自Attribute和过滤器接口的类型

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace WebApplication1
{
    /// <summary>
    /// 自定义过滤器(只允许周日操作)
    /// </summary>
    public class SundayFilter : Attribute, IActionFilter
    {
        /// <summary>
        /// Action执行前的操作
        /// </summary>
        /// <param name="context"></param>
        public void OnActionExecuting(ActionExecutingContext context)
        {
            if (DateTime.Now.DayOfWeek != DayOfWeek.Sunday)
                context.Result = new ContentResult()
                {
                    Content = "Sorry only on sundays!"
                };
        }

        /// <summary>
        /// Action执行后的操作
        /// </summary>
        /// <param name="context"></param>
        public void OnActionExecuted(ActionExecutedContext context)
        {
            // do something after the action executes
        }
    }
}

使用过滤器

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using WebApplication1.Models;

namespace WebApplication1.Controllers
{
    public class HomeController : Controller
    {
        [SundayFilter]
        public IActionResult Index()
        {
            return View();
        }
    }
}

实例:自定义Action过滤器

public class CustomActionFilter:IActionFilter, IAsyncActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Action执行之前
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        //Action执行之后
    }

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // Action执行之前
        await next();
        // Action执行之后
    }
}

实例:具有实际功能的Action过滤器

会对将要传给Action的参数进行判断
如果不满足条件,则直接返回,不再继续执行后面的Action
并在HTTP响应中添加一个新Header项X-ParameterValidation
用于说明是否通过参数验证

public class ActionParameterValidationFilter: IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        var descriptor = context.ActionDescriptor as ControllerActionDescriptor;
        foreach (var parameter in descriptor.Parameters)
        {
            if (parameter.ParameterType == typeof(string))
            {
                var argument = context.ActionArguments[parameter.Name];
                if (argument.ToString().Contains(" "))
                {
                    context.HttpContext.Response.Headers["XParameterValidation"] = "Fail";
                    context.Result = new BadRequestObjectResult($"不能包含空格: {parameter. Name}");
                    break;
                }
            }
        }

        if (context.Result == null)
        {
            var resultContext = await next();
            context.HttpContext.Response.Headers["XParameterValidation"] = "Success";
        }
}
}

说明:
ActionParameterValidationFilter实现了IAsyncActionFilter接口
在OnActionExecutionAsync方法中
通过访问ActionExecutingContext对象的ActionDescriptor属性
得到将要执行Action的描述信息,
包括Action名称及所在的Controller的名称、HTTP方法约束、路由信息和参数等
在得到所有参数列表后,对每个参数进行遍历,如果它是字符串类型
并且它的值中包含空格,则在响应的消息头中添加新项X-ParameterValidation
并为ActionExecutingContext对象的Result属性赋值
该属性的类型是IActionResult
上述代码将BadRequestObjectResult对象赋给了此属性
在过滤器中,只要为ActionExecutingContext对象的Result属性赋一个非空值
就会中断过滤器的处理管道
这样会使当前Action以及后续的Action过滤器都不再继续执行
如果没有包含空格的参数,则执行Action 并在执行完成后添加自定义消息头

待合并

[RequireHttps]

必须HTTPS请求。

实例:使用缓存过滤器

设置缓存10分钟

[ResponseCache(Duration = 600)]
public IActionResult Index()
{
    return Content(DateTime.Now.ToShortTimeString());
}

设置不要进行缓存

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
    return View();
}

实例:设置只能使用https访问

[RequireHttps]
public IActionResult SecurePage()
{
    return View();
}

实例:控制器的Action开启token验证

在Action上使用[ValidateAntiForgeryToken]注解

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Recipe03(Contact model)
{
    if (ModelState.IsValid)
    {
        return View("Recipe03Thanks", model);
    }
    return View(model);
}

在View中或者提交过来的数据中需要开启Token字段:

<form asp-action="A" asp-anti-forgery="true" method="post">
</form>
posted @ 2022-11-24 10:12  重庆熊猫  阅读(2113)  评论(0编辑  收藏  举报