问题
如何在ASP.NET Core的MVC请求管道之前和之后运行代码。
解
在一个空的项目中,更新 Startup 类以添加MVC的服务和中间件。
-
publicvoid ConfigureServices
-
(IServiceCollection services)
-
{
-
services.AddMvc();
-
}
-
publicvoid Configure(
-
IApplicationBuilder app,
-
IHostingEnvironment env)
-
{
-
app.UseMvc(routes =>
-
{
-
routes.MapRoute(
-
name: "default",
-
template: "{controller=Home}/{action=Index}/{id?}");
-
});
-
}
添加类来实现过滤器。
-
publicclass ParseParameterActionFilter : Attribute, IActionFilter
-
{
-
publicvoid OnActionExecuting(ActionExecutingContext context)
-
{
-
object param;
-
if (context.ActionArguments.TryGetValue("param", out param))
-
context.ActionArguments["param"] = param.ToString().ToUpper();
-
else
-
context.ActionArguments.Add("param", "I come from action filter");
-
}
-
publicvoid OnActionExecuted(ActionExecutedContext context)
-
{
-
}
-
}
在Home控制器中添加一个使用Action过滤器的操作方法,
-
[ParseParameterActionFilter]
-
public IActionResult ParseParameter(string param)
-
{
-
return Content($"Hello ParseParameter. Parameter: {param}");
-
}
浏览到 / Home / ParseParameter; 你会看到的 -
讨论
筛选器在选择执行操作方法后运行。MVC为授权和缓存等内容提供了内置的过滤器。自定义过滤器对于封装要在动作方法之前或之后运行的可重用代码非常有用。
过滤器可能导致结果短路,即停止运行中的代码,并将结果返回给客户端。他们也可以通过服务容器注入 服务,这使得他们非常灵活。
过滤器接口
创建自定义过滤器需要实现您所需的过滤器类型的接口。大多数过滤器类型有两种类型的接口 - 同步和异步。
-
publicclass HelloActionFilter : IActionFilter
-
{
-
publicvoid OnActionExecuting(ActionExecutingContext context)
-
{
-
// runs before action method
-
}
-
publicvoid OnActionExecuted(ActionExecutedContext context)
-
{
-
// runs after action method
-
}
-
}
-
publicclass HelloAsyncActionFilter : IAsyncActionFilter
-
{
-
public async Task OnActionExecutionAsync(
-
ActionExecutingContext context,
-
ActionExecutionDelegate next)
-
{
-
// runs before action method
-
await next();
-
// runs after action method
-
}
-
}
您可以通过在上下文参数(对于异步过滤器,不要调用下一个 委托)中设置Result (类型 IActionResult)属性 来短路过滤器管道 。
-
publicclass SkipActionFilter : Attribute, IActionFilter
-
{
-
publicvoid OnActionExecuting(ActionExecutingContext context)
-
{
-
context.Result = new ContentResult
-
{
-
Content = "I'll skip the action execution"
-
};
-
}
-
publicvoid OnActionExecuted(ActionExecutedContext context)
-
{ }
-
}
-
[SkipActionFilter]
-
public IActionResult SkipAction()
-
{
-
return Content("Hello SkipAction");
-
}
对于结果过滤器,可以通过 在上下文参数上设置取消属性并发送响应来进行短路 。
-
public void OnResultExecuting(ResultExecutingContext context)
-
{
-
context.Cancel = true ;
-
context.HttpContext.Response.WriteAsync(“我将跳过结果执行” );
-
}
-
[SkipResultFilter]
-
public IActionResult SkipResult()
-
{
-
返回 内容(“Hello SkipResult” );
-
}
过滤器属性
MVC提供了可以继承的抽象基类来创建自定义过滤器。这些抽象类继承自 Attribute 类,因此可以用来装饰控制器和操作方法。
-
ActionFilterAttribute
-
ResultFilterAttribute
-
ExceptionFilterAttribute
-
ServiceFilterAttribute
-
TypeFilterAttribute
过滤器类型
在过滤器管道的不同阶段有各种类型的过滤器。下面,官方文件中的一个数字说明了顺序:
授权
这是运行的第一个过滤器,并将对未经授权的用户的请求短路。他们只有一个方法(不同于其他大多数具有执行 和 执行 方法的过滤器 )。通常情况下,您不会编写自己的授权过滤器,内置过滤器调用到框架的授权机制中。
资源
它们在模型绑定之前运行,可以用来改变它的行为。而且,它们在结果生成后运行,可用于缓存等。
行动
它们在动作方法之前和之后运行,因此对操作动作参数或其结果非常有用。提供给这些过滤器的上下文让您可以操作动作参数,控制器和结果。
例外
它们可以在写入响应之前用于未处理的异常。异常处理中间件适用于大多数场景,但是如果您想根据调用的操作以不同方式处理错误,则可以使用此过滤器。
结果
如果结果成功,它们在执行操作方法的结果之前和之后运行。他们可以用来操纵结果的格式。
过滤范围
可以在不同级别的范围添加过滤器:Action,Controller和Global。属性用于动作和控制器级作用域。对于全局范围的过滤器,您需要添加它们以 在启动时配置服务时 过滤MvcOptions的集合 ,
-
services.AddMvc(options =>
-
{
-
// by instance
-
options.Filters.Add(new AddDeveloperResultFilter("Tahir Naushad"));
-
// by type
-
options.Filters.Add(typeof(GreetDeveloperResultFilter));
-
});
过滤器按顺序执行
-
的 执行 方法是首先呼吁全球>控制器>动作过滤器。
-
然后 为Action> Controller> Global过滤器调用已执行的方法。
过滤依赖注入
为了使用需要在运行时注入依赖关系的过滤器,您需要通过Type添加它们。您可以将它们全局添加(如上所述),但是,如果要将它们应用于操作或控制器(作为属性),则有两个选项:
ServiceFilterAttribute
此属性使用服务容器检索过滤器。要使用它,
创建一个使用依赖注入的过滤器 ,
-
publicclass GreetingServiceFilter : IActionFilter
-
{
-
private readonly IGreetingService greetingService;
-
public GreetingServiceFilter(IGreetingService greetingService)
-
{
-
this.greetingService = greetingService;
-
}
-
publicvoid OnActionExecuting(ActionExecutingContext context)
-
{
-
context.ActionArguments["param"] =
-
this.greetingService.Greet("James Bond");
-
}
-
publicvoid OnActionExecuted(ActionExecutedContext context)
-
{ }
-
}
将过滤器添加到服务容器
-
services.AddScoped<GreetingServiceFilter>();
使用ServiceFilterAttribute应用它
-
[ServiceFilter(typeof(GreetingServiceFilter))]
-
public IActionResult GreetService(string param)
TypeFilterAttribute
该属性不需要在服务容器中注册过滤器,并使用ObjectFactory 委托启动类型 。使用它
创建一个使用依赖注入的过滤器
-
publicclass GreetingTypeFilter : IActionFilter
-
{
-
private readonly IGreetingService greetingService;
-
public GreetingTypeFilter(IGreetingService greetingService)
-
{
-
this.greetingService = greetingService;
-
}
-
publicvoid OnActionExecuting(ActionExecutingContext context)
-
{
-
context.ActionArguments["param"] = this.greetingService.Greet("Dr. No");
-
}
-
publicvoid OnActionExecuted(ActionExecutedContext context)
-
{ }
-
}
应用它使用 TypeFilterAttribute。
-
[TypeFilter(typeof(GreetingTypeFilter))]
-
public IActionResult GreetType1(string param)
你也可以从TypeFilterAttribute继承 ,然后在没有TypeFilter的情况下 使用,
-
publicclass GreetingTypeFilterWrapper : TypeFilterAttribute
-
{
-
public GreetingTypeFilterWrapper() : base(typeof(GreetingTypeFilter))
-
{ }
-
}
-
[GreetingTypeFilterWrapper]
-
public IActionResult GreetType2(string param)
更多精彩文章请关注我们的微信公众号FocusDotCore: