龙空技术网

ASP.NET Core 过滤器

opendotnet 314

前言:

而今姐妹们对“aspnet的文献”大约比较关心,咱们都需要分析一些“aspnet的文献”的相关内容。那么小编也在网摘上收集了一些对于“aspnet的文献””的相关资讯,希望各位老铁们能喜欢,你们一起来了解一下吧!

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

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

1 创建例子项目

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

2 内置过滤器-[RequireHttps]

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

现在,运行应用程序并且在浏览器打开一个非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在浏览器,这次它不会跳转,我们将看到下面消息显示在浏览器上:

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

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

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

我们尝试调用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

IAuthorizationFilterIAsyncAuthorizationFilter

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

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

当我们在浏览器中打开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"; } }}

运行结果如下:

Action过滤器也能通过继承自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试图被调用,如下图所示

我们通过实现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 - 在该方法中开始Stopwatch2 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视图将被调用,并且显示执行时间

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

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 == ) throw new Exception("Error Id cannot be "); 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 as an exception is raised ,该异常信息通过过滤器捕获到,现在进入URL- /Home/Exception/5 没有异常发生,你将会看到如下信息The value is 5

如下图所示:

源代码地址

参考文献

标签: #aspnet的文献 #net core过滤器和中间件 #aspnet timer