ASP.NET Core 过滤器

ASP.NET Core 中的过滤器运行在请求处理管道特定阶段前或者后,ASP.NET Core内置了很多过滤器,例如:授权,日志,缓存,异常处理等等,过滤器可以避免我们项目中出现重复的代码

ASP.NET Core 中间件和过滤器有什么不同呢?中间件可以操作进入.NET Core应用程序的每一个请求,另外过滤器仅仅操作进入 MVC 管道的请求,中间件不能访问HttpContext,除非我们预先添加进去值,请查看该链接https://www.thetechplatform.com/

post/middleware-and-filters-power-in-asp-net-core

1 创建例子项目

我们通过一个例子来了解过滤器, 在Visual Studio创建一个MVC项目,命名为AspNetCore.Filters

4567d21de8a9cc48d164410e00edd7c9.png

2 内置过滤器-[RequireHttps]

[RequireHttps] 特性是内置的Filter,使用在控制器或者方法上阻止非HTTPS的请求,创建一个MVC模版的应用程序,它自动将所有向非https端点发出的请求重定向到它们各自的https端点,在Properties目录下打开lauchSettings.json文件,在applicationUrl属性下有2个urls,第二个url是非https的,在我们应用程序中为http://localhost:5192

2094dc2bda7a895ccbc7523380eada0b.png

现在,运行应用程序并且在浏览器打开一个非https的url地址,我们将看到浏览器立即导航我们到一个https的url地址,为了理解这个[RequireHttps]过滤器,我们打开Program.cs文件,并且注释掉如下代码:

//app.UseHttpsRedirection();

现在,重新运行应用程序并且打开非https的url在浏览器,这次它不会跳转,接下来,我们修改Home控制器中的Index方法返回如下字符串:

public string Index()
{
   return "This is the Index action on the Home controller";
}

现在,重新运行应用程序并且打开非https的url在浏览器,这次它不会跳转,我们将看到下面消息显示在浏览器上:

ce31cf1b1cc034b0ffefd9ffa6884c05.png

现在让这个方法仅仅能接收HTTPS的请求,我们将[RequireHttps]特性添加到方法上

[RequireHttps]
public string Index()
{
    return "This is the Index action on the Home controller";
}

现在我们限制这个方法仅仅能访问HTTPS的请求,我们将[RequireHttps]特性添加到方法上,重新运行应用程序并且再次打开非https url,我们将获取如下页面

d0761a5cba763ebad3a5063cf2faa124.png

我们尝试调用action方法使用非https请求,但是[RequireHttps]阻止了该行为

了解如何进行跳转并且我们如何使用RequireHttps过滤我们的请求,[RequireHttps]内置过滤器也能应用到Controllers类上, 在这种情况下所有控制器的方法将获取这个特性并且将阻止所有非https请求,在下面例子中,所有的3个action方法将获取RequireHttps特性

[RequireHttps]
public class HomeController : Controller
{
    public string Index()
    {
        return "This is the Index action";
    }
    public string List()
    {
        return "This is the List action";
    }
    public string Hello()
    {
        return "This is the Hello action";
    }
}

3 ASP.NET Core MVC中常用过滤器

在.NET Core中有多少类型的过滤器呢?在.NET Core最常用的由4中类型过滤器Authorization, Action, Result 和 Exception,每个过滤器可以同步和异步两种工作方式,下面表格描述他们详细 

过滤器

接口

描述

Authorization

IAuthorizationFilter

IAsyncAuthorizationFilter

使用它申请授权和安全策略

Action

IActionFilter, 

IAsyncActionFilter

在action方法之前或者之后指定执行的具体工作

Result

IResultFilter, 

IAsyncResultFilter

用于在操作方法的结果之前或之后立即执行指定工作

Exception

IExceptionFilter, 

IAsyncExceptionFilter

使用它来处理异常

如何在.NET Core中创建自定义过滤器?我们创建一个自定义过滤器继承自Attribute与此同时还必须继承与其匹配的过滤器接口,接下来,我们将过滤器应用到Controller或者Action方法,注意每个过滤器只能够实现Synchronous或者Asynchronous两个接口中的一个

4 过滤器执行顺序

过滤器按照下面顺序执行:

1 Authorization 过滤器第一个执行

2 Action过滤器其次 

3 Result过滤器最后执行

Exception过滤器只有在发生异常时执行

5 ASP.NET Core MVC Authorization 过滤器

什么是Authorization过滤器? Authorization 过滤器被使用针对授权和创建安全策略,在这些过滤器中,它是第一个在管道中运行的过滤器,授权过滤器实现了 IAuthorizationFilter或者IAsyncAuthorizationFilter 接口

IAuthorizationFilter 接口定义

public interface IAuthorizationFilter : IFilterMetadata
{
   void OnAuthorization(AuthorizationFilterContext context);
}

IAsyncAuthorizationFilter 接口定义如下:

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

IAsyncAuthorizationFilter接口使用创建异步的授权过滤器,接口的方法是- OnAuthorization() 和 OnAuthorizationAsync() 写代码授权进入的请求,参数-AuthorizationFilterContext 表示接收的描述请求的上下文数据,AuthorizationFilterContext 包含一个属性名字为Result的属性(类型是IActionResult),授权过滤器会设置该属性

5.1 自定义Authorization过滤器

让我们创建一个自定义授权过滤器,例如限制非https协议的请求,这个过滤器能被应用到任何Action方法或者Controller类上,创建一个文件夹叫CustomFilters 在应用程序的根目录下并且添加一个新类命名为HttpOnly.cs,接下来添加如下代码:

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

注意HttpsOnly类继承自Attribute类,为了能够在Controllers和Action方法上使用该特性,同时也继承了IAuthorizationFilter接口为了创建一个Authorization过滤器

在OnAuthorization方法内实现我们想要做的工作,判断进入的请求是否是https,当请求是非https,我们将设置context.Result为403 forbidden,这将阻止Action或者Controller执行,用户将在浏览器中看到forbidden错误消息

现在应用HttpsOnly特性在HomeControllers.cs的Index方法:

[HttpsOnly]
public string Index()
{
   return "This is the Index action on the Home controller";
}

最后,运行应用程序并且打开非https地址,我们将会看到HTTP ERROR 403, 这是因为我们请求只允许非https url

bbde9bf6be59198fe185f33dd00031cd.png

当我们在浏览器中打开https的url时,Authorization过滤器不会阻止执行,我们不会设置AuthorizationFilterContext的Result属性,Index方法内部的代码将会执行,这个例子很好解释了授权过滤器的工作原理,接下来我们将创建一个Action 过滤器

6 ASP.NET Core MVC Action 过滤器

什么是Action过滤器? Action过滤器在一个Action 方法的前或者后执行,该过滤器位于管道的第二位执行,在授权过滤器之后执行,Action过滤器继承自IActionFilter或者IAsyncActionFilter接口的任何一个

IActionFilter接口定义如下:

public interface IActionFilter : IFilterMetadata
{
    void OnActionExecuting(ActionExecutingContext context);
    void OnActionExecuted(ActionExecutedContext context);
}

我们在Action方法上使用Action 过滤器时,OnActionExecuting在Action方法执行之前被调用,OnActionExecuted在Action方法执行之后被调用

OnActionExecuting 方法有一个ActionExecutingContext的参数类型,ActionExecutingContext对象有重要的属性如下: 

名称

描述

Controller

将要调用的Action方法的控制器的名称

Result

当这个属性设置为IActionResult的值时,框架会呈现IActionResult,阻止调用Action方法

OnActionExecuted 方法有一个ActionExecutedContext 类型的参数,ActionExecutedContext 重要的属性如下:

名称

描述

Controller

将要调用的Action方法的控制器的名称

Exception

包含了在Action方法中发生的异常

ExceptionHandled

将该属性设置为true时,阻止异常传播

Result

返回IActionResult,我们可以用自己的业务逻辑来修改或者替换它

6.1 自定义Action过滤器

现在将创建一个Action的过滤器,用他们测量Action方法的执行时间,我们在OnActionExecuting方法中启动一个timer并且在OnActionExecuted停止该方法

创建一个TimeElapsed.cs类在CustomFilters文件夹下并且添加如下代码:

namespace AspNetCore.Filters.CustomFilters
{
    public class TimeElapsed : Attribute, IActionFilter
    {
        private Stopwatch timer;
        public void OnActionExecuting(ActionExecutingContext context)
        {
            timer.Start();
        }
        public void OnActionExecuted(ActionExecutedContext context)
        {
            timer.Stop();
            string result = " Elapsed time: " + $" {timer.Elapsed.TotalMilliseconds} ms";
            IActionResult iActionResult = context.Result;
            ((ObjectResult)iActionResult).Value += result;
        }
    }
}

创建Stopwatch类计算方法的执行时间, 在OnActionExecuting方法中开始执行,在OnActionExecuted方法中结束执行,接下使用ActionExecutedContext对象的Result属性获取到Action方法的执行结果,他包含了一个我们之前在Home控制器的Index方法设置的字符串,最后,我们转换它到ObjectResult类型并且添加时间到它的Value属性,为了使用filter,添加[TimerElapsed]特性到Home控制器的Index 方法,显示如下:

namespace AspNetCore.Filters.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;


        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }
        [HttpsOnly]
        [TimeElapsed]
        public string Index()
        {          
            return "This is the Index action on the Home controller";
        }
    }
}

运行结果如下:

cba2766e81bf9bab05830dba7576b002.pngAction过滤器也能通过继承自IAsyncActionFilter接口创建,接口定义如下:

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

我们看到该接口中只有一个方法,ActionExecutingContext对象提供了上下文对象,ActionExectionDelegate 表示一个action方法(或者下一个Filter)

现在我们使用异步版本的接口重新创建一个TimeElapsed过滤器,代码如下:

namespace AspNetCore.Filters.CustomFilters
{
    public class TimeElapsedAsync : Attribute, IAsyncActionFilter
    {
        public async Task OnActionExecutionAsync(ActionExecutingContext context, 
                                           ActionExecutionDelegate next)
        {
            Stopwatch stopwatch = Stopwatch.StartNew();
            await next();
            stopwatch.Stop();
            string result = "<div>Elapsed time: "
                + $"{stopwatch.Elapsed.TotalMilliseconds} ms</div>";
            byte[] bytes = Encoding.ASCII.GetBytes(result);
            await context.HttpContext.Response.Body.WriteAsync(bytes, 0, bytes.Length);


        }
    }
}

7 ASP.NET Core Result 过滤器

在Action方法成功执行前或者后会执行Result过滤器,Result过滤器位于过滤器管道中的第三位置,在action过滤器之后执行,通过继承自IResultFilter和IAsyncResultFilter接口创建Result过滤器

IResultFilter接口定义如下:

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

注意:Result过滤器和Action过滤器定义类似

IResultFilter接口有2个方法-OnResultExecuting和OnResultExecuted,OnResultExecuting在action方法的结果处理之前调用,OnResultExecuted 在action方法的结果处理之后调用,OnResultExecuting 方法有ResultExecutingContext 的参数类型,该参数的属性列表如下:

名称

描述

Controller

被调用action方法的控制器的名称

Result

这是一个IActionResult类型的属性,包含了action方法返回的IActionResult对象

Cancel

设置这个属性为true将会阻止action结果的处理并且返回404错误

OnResultExecuted方法有一个ResultExecutedContext类型的参数,属性列表如下:

名称

描述

Controller

被调用action方法的控制器的名称

Canceled

只读属性表示请求是否被取消

Exception

包含在action方法中抛出的异常

ExceptionHandled

将该属性设置为true时,异常不会传播

Result

IActionResult只读属性由含Action方法生成 

7.1 Result 过滤器例子

我们现在创建一个Result过滤器例子,当调用Action方法时改变View的呈现方式,因此创建一个ChangeView.cs的类在CustomFilters文件夹内使用下面代码:

namespace AspNetCore.Filters.CustomFilters
{
    public class ChangeView : Attribute, IResultFilter
    {
        public void OnResultExecuted(ResultExecutedContext context)
        {


        }
        public void OnResultExecuting(ResultExecutingContext context)
        {
            context.Result = new ViewResult
            {
                ViewName = "List"
            };
        }
    }
}

我们在OnResultExecuting()方法方法中设置ViewName为List 在, 当我们把这个过滤器使用到action方法时,List试图会替换默认试图来呈现,让我们做个测试,创建一个新的Action方法叫Message在Home控制器中,如下所示,我们把[ChangeView]过滤器使用到该Action方法上:

[ChangeView]
 public IActionResult Message() 
 {
    return View();
 }

该Action方法默认调用Message.cshtml,现在我们在Views->Shared目录下添加两个试图:

Message.cshtml

<h2>Message</h2>
<p>This is Message View</p>

List.cshtml

<h2>List</h2>
<p>This is Message View</p>

运行应用程序测试一下该特性,运行应用程序并且进入URL - /Home/Message 我们将看到List试图被调用,如下图所示

7b8c9e7b230d3235d9cdd2417af5109a.png

我们通过实现IAsyncResultFilter异步接口创建ChangView Result异步过滤器

 IAsyncResultFilter定义如下:

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

我们在CustomFilters文件下添加一个ChangeViewAsync.cs类:

namespace AspNetCore.Filters.CustomFilters
{
    public class ChangeViewAsync : Attribute, IAsyncResultFilter
    {
        public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
        {
            context.Result = new ViewResult()
            {
                ViewName = "List"
            };
            await next();
        }
    }
}

IAsyncResultFilter只有一个方法OnResultExecutionAsync,参数有一个ResultExecutingContext类型我们使用它设置ViewName为List

ResultExecutionDelegate参数是一个异步代理,异步返回一个ResultExecutedContext对象,该对象表示Action方法执行结果或者下一个中间件执行结果,我们手动调用代理使用await next()以至于action结果能被呈现,现在进入Home控制器将Message方法的特性为[ChangeViewAsync]

[ChangeViewAsync]
public IActionResult Message() 
{
    return View();
}

8 混合Action/Result 过滤器

混合过滤器能容易得共享数据从Action到Result阶段,创建混合过滤器最简单的方式是继承ActionFilterAttribute类,该类实现这两个过滤器类型的接口,创建一个新的类文件叫HybridActRes.cs在CustomFilters文件夹下:

public class HybridActRes: ActionFilterAttribute
    {
        Stopwatch stopwatch;
        public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            stopwatch=Stopwatch.StartNew();
            await next();
        }
        public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
        {
            stopwatch.Stop();
            context.Result = new ViewResult()
            {
                ViewName = "ShowTime",
                ViewData = new Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary(
                    new EmptyModelMetadataProvider(),
                    new ModelStateDictionary())
                {
                    Model = "Elapsed time: " + $"{stopwatch.Elapsed.TotalMilliseconds} ms"
                }
            };
            await next();
        }
    }

混合过滤器有2个方法:

1 OnActionExecutionAsync - 在该方法中开始Stopwatch

2 OnResultExecutionAsync - 在该方法中停止Stopwatch并且将View切换到ShowTime并且Model被赋值为一个字符串显示执行的时间

接下来,在Home控制器中创建一个List方法使用下面代码:

[HybridActRes]
public IActionResult List() 
{
    return View();
}

最后在Views->Shared目录下创建一个ShowTime的视图,代码如下:

@model string
<h2>Show Time</h2>
@Model

接下来我们测试一下该功能并且导航到URL- /Home/List,我们将看到ShowTime视图将被调用,并且显示执行时间

8db2bf8b869d6000c0911138683847dc.png

因此,使用混合过滤器我们能在单独的文件中实现两个功能

9 Exception 过滤器

Exception 过滤器允许在不同写try & catch代码块的情况下来捕获异常,实现IExceptionFilter或者IAsyncExceptionFilter接口IAsyncExceptionFilter接口使用创建异步异常过滤器

IExceptionFilter接口的定义

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

IAsyncExceptionFilter接口的定义

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

在这两个接口,在OnException & OnExceptionAsynccontext 方法参数中提供了ExceptionContext对象,ExceptionContext 类有如下属性

名称

描述

Exception

这个属性包含抛出的异常

ExceptionDispatchInfo

包含了异常堆栈的详细

ExceptionHandled

只读的属性,异常是否处理

Result

这个属性设置IActionResult生成response

9.1 Exception 过滤器例子

在CustomFilters文件夹下创建一个CatchError.cs类, 添加下面代码:

public class CatchError : Attribute, IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        context.Result = new ViewResult
        {
            ViewData = new Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary(
                new EmptyModelMetadataProvider(),
                new ModelStateDictionary())
            {
                Model = context.Exception.Message
            }
        };
    }
}

我们实现了一个CatchError的异常过滤器,我们将方法应用[CatchError]特性,该方法发生异常时,过滤器会自动捕获异常,在OnException()方法内我们给了Model属性赋值为context.Exception.Message,Model值将会显示在View,现在在Home控制器中创建一个新的Action方法叫Exception并且添加CatchError特性:

[CatchError]
public IActionResult Exception(int? id)
{
    if (id == null)
        throw new Exception("Error Id cannot be null");
    else
        return View($"The value is {id}");
}

如果id为空时会触发一个异常否则将会在View中显示id的值,最后在View->Home文件夹下创建一个Exception试图

@model string
<h2>Exception</h2>
@Model

接下来我们验证Exception过滤器,运行应用程序并且进入URL- /Home/Exception,你将会看到显示如下信息-Error Id cannot be null as an exception is raised ,该异常信息通过过滤器捕获到,现在进入URL- /Home/Exception/5 没有异常发生,你将会看到如下信息The value is 5

如下图所示:

ca92ac230ed83910b52525a7fef625d0.png

2b58f0e8b52455ffc17ff329e86ae6d3.png

源代码地址

https://github.com/bingbing-gui/Asp.Net-Core-Skill/tree/master/Fundamentals/AspNetCore.Filters/AspNetCore.Filters

参考文献

https://www.yogihosting.com/aspnet-core-filters/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值