记录学习成果,以便温故而知新
本文以命名空间Microsoft.AspNetCore.Mvc.Filters下的过滤器学习的过程做一个记录。广大网友们都说是过滤器,Java里貌似也说是过滤器,所以就顺着大家叫了。但是官网上却是叫“筛选器”。
Microsoft.AspNetCore.Mvc.Filters 命名空间官网:https://learn.microsoft.com/zh-cn/dotnet/api/microsoft.aspnetcore.mvc.filters?view=aspnetcore-7.0
讲实话,微软的官网现在感觉有点凌乱,有些看似官网的却是志愿者还是啥的写的内容。所以如果有什么差错还请谅解。
广大网友们所说的五种过滤器,当然它们都有对应的异步过滤器。由于学习是循序渐进的,先学习同步的,等掌握好了以后再去学习异步的。
环绕操作执行的筛选器。
两个方法:
1.OnActionExecuted(ActionExecutedContext)
在操作执行后、操作结果之前调用。
2.OnActionExecuting(ActionExecutingContext)
在操作执行之前调用,在模型绑定完成后调用。
确认请求授权的筛选器。
一个方法:
OnAuthorization(AuthorizationFilterContext)
在筛选器管道的早期调用,以确认请求是否已获得授权。
在操作引发 Exception后运行的筛选器。
一个方法:
OnException(ExceptionContext)
在操作引发 Exception后调用 。
包含模型绑定执行的筛选器、操作 (和筛选器) 以及操作结果 (和筛选器) 。
两个方法:
1.OnResourceExecuted(ResourceExecutedContext)
执行资源筛选器。 在执行管道的其余部分后调用。
2.OnResourceExecuting(ResourceExecutingContext)
执行资源筛选器。 在执行管道的其余部分之前调用。
包含操作执行结果的筛选器已成功从操作返回。
IResultFilter 如果授权筛选器或资源筛选器使请求短路以阻止操作的执行,则不会执行 和 IAsyncResultFilter 实例。 IResultFilter. IResultFilter 在异常筛选器通过生成操作结果处理异常的情况下,也不会执行 和 IAsyncResultFilter 实现。
两个方法:
1.OnResultExecuted(ResultExecutedContext)
在操作结果执行之后调用。
2.OnResultExecuting(ResultExecutingContext)
在操作结果执行之前调用。
官网中还有另外两种过滤器,不知道广大网友为什么不介绍,只能揣测是不常用。简单介绍如下:
一个筛选器,指定它应运行的相对顺序。
一个筛选器,用于包围页面处理程序方法的执行。 仅当对处理程序的类型进行修饰时,才会执行此筛选器,而不是在单个处理程序方法上执行。
很奇怪的是看到很多网友介绍的文章在定义过滤器特性时都没有继承现成的特性,而是实现的接口。
异步包围操作的执行和操作结果的抽象筛选器。 子类应替代 、 OnActionExecuted(ActionExecutedContext) 或 OnActionExecutionAsync(ActionExecutingContext, ActionExecutionDelegate) (但不替代OnActionExecutionAsync(ActionExecutingContext, ActionExecutionDelegate)OnActionExecuting(ActionExecutingContext))以及其他两个之一。 同样,子类应替代 、 OnResultExecuted(ResultExecutedContext) 或 OnResultExecutionAsync(ResultExecutingContext, ResultExecutionDelegate) (但不能OnResultExecutionAsync(ResultExecutingContext, ResultExecutionDelegate))OnResultExecuting(ResultExecutingContext)以及其他两个之一。
在操作引发 Exception后异步运行的抽象筛选器。 子类必须替代 或,OnExceptionAsync(ExceptionContext)但不能同时重写OnException(ExceptionContext)两者。
我再次确认了上面这段的引用,官网是:
只能无语(我竟无言以对)!!!
异步包围操作结果执行的抽象筛选器。 子类必须替代 、 OnResultExecuted(ResultExecutedContext) 或 OnResultExecutionAsync(ResultExecutingContext, ResultExecutionDelegate) (但不能OnResultExecutionAsync(ResultExecutingContext, ResultExecutionDelegate)替代OnResultExecuting(ResultExecutingContext))和另外两个之一。
以上内容看似洋洋洒洒,实际上主要来源于官网。对于这些知识的定义,也只能相信官网,不可能自己发挥的。下面是代码演示,就有了发挥的空间。
本文的演示环境是Win10,目标框架是.NET Core 2.1,建的是ASP.NET Core Web 应用程序。不同的环境可能会有差别。
namespace TestFilter.Filter
{
public class ActionFilter1Attribute : Attribute, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
Debug.WriteLine("ActionFilter1Attribute OnActionExecuted");
}
public void OnActionExecuting(ActionExecutingContext context)
{
Debug.WriteLine("ActionFilter1Attribute OnActionExecuting");
}
}
}
namespace TestFilter.Filter
{
public class ActionFilter2Attribute : Attribute, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
Debug.WriteLine("ActionFilter2Attribute OnActionExecuted");
}
public void OnActionExecuting(ActionExecutingContext context)
{
Debug.WriteLine("ActionFilter2Attribute OnActionExecuting");
}
}
}
定义了两个过滤器是想看同时注册时的调用顺序,如下测试代码:
// GET: api/User/ShowAction1
[HttpGet("ShowAction1")]
[ActionFilter1, ActionFilter2]
public IActionResult ShowAction1()
{
ContentResult cr = new ContentResult();
cr.Content = "ShowAction1";
return cr;
}
// GET: api/User/ShowAction2
[HttpGet("ShowAction2")]
[ActionFilter2, ActionFilter1]
public IActionResult ShowAction2()
{
ContentResult cr = new ContentResult();
cr.Content = "ShowAction2";
return cr;
}
控制台输出:
两张截图对比一下,结果很清晰的,所以也用不着总结了。
这里是把特性加在方法上,是给单个方法注册过滤器,下面类似情况不再重复说明。
确认授权过滤器特性定义了一个属性,是像简单演示一下权限是如何传参的。记得Java的shiro框架中logical来说明权限数组是“与”逻辑还是“或”逻辑,这里就不细说了,只要明白套路就行。所谓“运用之妙,存乎一心”。
namespace TestFilter.Filter
{
public class AuthorizationFilterAttribute : Attribute, IAuthorizationFilter
{
///
/// 权限
///
private string[] Privilege { get; set; }
public AuthorizationFilterAttribute(params string[] privilege)
{
Privilege = privilege;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
Debug.WriteLine("权限数量:" + Privilege.Length);
Debug.WriteLine("AuthorizationFilter OnAuthorization");
}
}
}
测试代码如下:
// GET: api/User/ShowAuthorization1
[HttpGet("ShowAuthorization1")]
[AuthorizationFilter]
public IActionResult ShowAuthorization1()
{
ContentResult cr = new ContentResult();
cr.Content = "Authorization1";
return cr;
}
// GET: api/User/ShowAuthorization2
[HttpGet("ShowAuthorization2")]
[AuthorizationFilter("Admin", "User")]
public IActionResult ShowAuthorization2()
{
ContentResult cr = new ContentResult();
cr.Content = "Authorization2";
return cr;
}
控制台输出:
这里用可变长参数给特性传参,模拟传"Admin"与"User"两类权限。
由于异常处理通常是全局的,所以就没有定义成特性。
namespace TestFilter.Filter
{
public class ExecptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
Debug.WriteLine(context.Exception);
context.Result = new ObjectResult("ExceptionHandled ");
context.ExceptionHandled = true;
}
}
}
在Startup.cs文件中ConfigureServices方法里注册全局过滤器:
services.AddMvc(option => {
option.Filters.Add<ExecptionFilter>();//全局异常
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
测试代码如下:
// GET: api/User/ShowException
[HttpGet("ShowException")]
public IActionResult ShowException()
{
throw new Exception("ShowException");
}
控制台输出如图:
在集成开发环境中要点一下“继续”才能看到控制台的输出,否则流程会被阻塞。
namespace TestFilter.Filter
{
public class ResourceFilterAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuted(ResourceExecutedContext context)
{
Debug.WriteLine("ResourceFilterAttribute OnResourceExecuted");
}
public void OnResourceExecuting(ResourceExecutingContext context)
{
Debug.WriteLine("ResourceFilterAttribute OnResourceExecuting");
}
}
}
测试代码如下:
// GET: api/User/ShowResource
[HttpGet("ShowResource")]
[ResourceFilter]
public IActionResult ShowResource()
{
ContentResult cr = new ContentResult();
cr.Content = "ShowResource";
return cr;
}
namespace TestFilter.Filter
{
public class ResultFilter1Attribute : Attribute, IResultFilter
{
public void OnResultExecuted(ResultExecutedContext context)
{
Debug.WriteLine("ResultFilterAttribute OnResultExecuted");
}
public void OnResultExecuting(ResultExecutingContext context)
{
Debug.WriteLine("ResultFilterAttribute OnResultExecuting");
}
}
}
测试代码如下:
// GET: api/User/ShowResult1
[HttpGet("ShowResult1")]
[ResultFilter1]
public IActionResult ShowResult1()
{
ContentResult cr = new ContentResult();
cr.Content = "ShowResult1";
return cr;
}
测试代码:
// GET: api/User/Comprehensive
[HttpGet("Comprehensive")]
[ActionFilter1]
[AuthorizationFilter]
[ResourceFilter]
[ResultFilter1]
public IActionResult Comprehensive()
{
ContentResult cr = new ContentResult();
cr.Content = "Comprehensive";
return cr;
}
控制台输出如图:
这张截图很能说明各种过滤器的执行顺序。
异常过滤器会阻断一些过滤器的执行,有兴趣的自己可以去测试一下。
这段代码来源于文章了解操作筛选器 (C#)“创建日志操作筛选器”一节,感觉很有趣,就借用了。
namespace TestFilter.Filter
{
public class LogActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Log("OnActionExecuting", filterContext.RouteData);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Log("OnActionExecuted", filterContext.RouteData);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Log("OnResultExecuting", filterContext.RouteData);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Log("OnResultExecuted", filterContext.RouteData);
}
private void Log(string methodName, RouteData routeData)
{
var controllerName = routeData.Values["controller"];
var actionName = routeData.Values["action"];
var message = String.Format("{0} controller:{1} action:{2}", methodName, controllerName, actionName);
Debug.WriteLine(message, "Action Filter Log");
}
}
}
测试代码:
namespace TestFilter.Controllers.Api
{
[Route("api/[controller]")]
[ApiController]
[LogActionFilter]
public class RoleController : ControllerBase
{
// GET: api/Role/Show
[HttpGet("Show")]
public IActionResult Show()
{
ContentResult cr = new ContentResult();
cr.Content = "Show";
return cr;
}
}
}
控制台输出如图:
这里的代码一是想介绍一下在类上注册过滤器;二是简单介绍获取方法的信息,其实还可以获取方法的传参与返回,限于篇幅就不展开了;三是想展示一下ActionFilterAttribute类可以覆盖的内容。
namespace TestFilter.Filter
{
public class ResultFilter : ResultFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext context)
{
Debug.WriteLine("ResultFilter OnResultExecuted");
}
public override void OnResultExecuting(ResultExecutingContext context)
{
Debug.WriteLine("ResultFilter OnResultExecuting");
}
}
}
测试代码:
// GET: api/User/ShowResult2
[HttpGet("ShowResult2")]
[ResultFilter]
public IActionResult ShowResult2()
{
ContentResult cr = new ContentResult();
cr.Content = "ShowResult2";
return cr;
}
因为考虑到异常一般是全局处理,所以没有研究ExceptionFilterAttribute,感觉如果项目定义自己的异常体系倒是有必要针对不同异常来做处理。