专栏首页不做码农的开发者【asp.net core 系列】10 实战之ActionFilter

【asp.net core 系列】10 实战之ActionFilter

0.前言

在上一篇中,我们提到了如何创建一个UnitOfWork并通过ActionFilter设置启用。这一篇我们将简单介绍一下ActionFilter以及如何利用ActionFilter,顺便补齐一下上一篇的工具类。

1. ActionFilter 介绍

ActionFilter全称是ActionFilterAttribute,我们根据微软的命名规范可以看出这是一个特性类,看一下它的声明:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public abstract class ActionFilterAttribute : Attribute, IActionFilter, IFilterMetadata, IAsyncActionFilter, IAsyncResultFilter, IOrderedFilter, IResultFilter

这是一个允许标注在类和方法上的特性类,允许多个标记,标注之后子类会继承父类的特性。然后,这个类是一个抽象类,所以我们可以通过继承ActionFilterAttribute来编写自己的ActionFilter。

1.1 ActionFilter的四个方法

对于一个ActionFilter而言,最重要的是它的四个方法:

public virtual void OnActionExecuted(ActionExecutedContext context);
public virtual void OnActionExecuting(ActionExecutingContext context);

public virtual void OnResultExecuted(ResultExecutedContext context);
public virtual void OnResultExecuting(ResultExecutingContext context);

上图是这四个方法在一次请求中执行的顺序。在一次请求真正执行之前,想要拦截这个请求,应该使用OnActionExecuting

为什么单独说这个呢?因为这个方法的出镜率很高,大多数时候都会使用这个方法进行请求过滤。

1.2 在ActionFilter中我们能做什么

我们来简单介绍一下,四个方法中的四种上下文类型,看一看里面有哪些我们可以利用的方法:

1.2.1 ActionExecutingContext

这是一个Action执行前的上下文,表示Action并未开始执行,但是已经获取到了控制器实例:

public class ActionExecutingContext : FilterContext
{
    public virtual IDictionary<string, object> ActionArguments { get; }
    public virtual object Controller { get; }
    public virtual IActionResult Result { get; set; }
}

ActionExecutingContext继承自FilterContext,我们暂且不关注它的父类,只看一下它自己的属性。

  • ActionArguments 表示Action的参数列表,这里面放着各种从用户接到请求参数以及其他中间处理程序添加的参数
  • Controller 表示执行该请求的控制器,在之前我们提过,asp.net core 对于控制器的限制很小,所以控制器什么类型都可能,故而这里使用object作为控制器类型
  • Result 执行结果,正常情况下,在此处获取这个属性的值没有意义。但是我们可以通过修改这个属性的值,来让我们拦截请求

1.2.2 ActionExecutedContext

ActionExecutedContext 表示Action执行完成后的上下文,这时候Action已经执行完成,我们可以通过这个获取Action执行结果:

public class ActionExecutedContext : FilterContext
{
    public virtual bool Canceled { get; set; }
    public virtual object Controller { get; }
    public virtual Exception Exception { get; set; }
    public virtual ExceptionDispatchInfo ExceptionDispatchInfo { get; set; }
    public virtual bool ExceptionHandled { get; set; }
    public virtual IActionResult Result { get; set; }
}

同样,继承自FilterContext,暂且忽略。

  • Canceled 表示是否被设置短路
  • Controller 处理请求的控制器
  • Exception 执行过程中是否发生异常,如果有异常则 有值,否则为Null
  • ExceptionHandled 异常是否被处理
  • Result 此处对Result进行修改不会屏蔽执行的ActionResult,但是可以向用户隐藏对应的实现

1.2.3 ResultExecutingContext

这是在Result渲染之前执行的上下文,这时候Action已经执行完毕,正准备渲染Result:

public class ResultExecutingContext : FilterContext
{
    public virtual bool Cancel { get; set; }
    public virtual object Controller { get; }
    public virtual IActionResult Result { get; set; }
}
  • Cancel 取消当前结果执行以及后续筛选器的执行
  • Controller 控制器
  • Result 处理结果

1.2.4 ResultExecutedContext

Result已经执行完成了,获取执行结果上下文:

public class ResultExecutedContext : FilterContext
{
    public virtual bool Canceled { get; set; }
    public virtual object Controller { get; }
    public virtual Exception Exception { get; set; }
    public virtual ExceptionDispatchInfo ExceptionDispatchInfo { get; set; }
    public virtual bool ExceptionHandled { get; set; }
    public virtual IActionResult Result { get; }
}

这个类与 ActionExecutedContext类似,就不做介绍了。

1.2.5 FilterContext

在上面的四个上下文都继承自 FilterContext,那么我们来看一下FilterContext中有哪些属性或者方法:

public abstract class FilterContext : ActionContext
{
    public virtual IList<IFilterMetadata> Filters { get; }
    public TMetadata FindEffectivePolicy<TMetadata>() where TMetadata : IFilterMetadata;
}

可以看到FilterContext继承了另一个ActionContext的类。小伙伴们应该对这个类要有一定的概念,这个类是Action的上下文类。它完整存在于一个Action的生命周期,所以有时候可以通过ActionContext进行Action级的数据传递(不推荐)。

那么,继续让我们回过头来看看ActionContext里有什么:

public class ActionContext
{
    public ActionDescriptor ActionDescriptor { get; set; }
    public HttpContext HttpContext { get; set; }
    public ModelStateDictionary ModelState { get; }
    public RouteData RouteData { get; set; }
}
  • ActionDescriptor 执行的Action描述信息,包括Action的显示名称、一些参数等,具体用到的时候,再为大伙详细说
  • HttpContext 可以通过这个属性获取此次请求的Request和Response对象
  • ModelState 模型校验信息, 这部分在后续再为小伙伴们细说
  • RouteData 路由信息,asp.net core 在处理请求时解析出来的路由信息,包括在程序中修改的路由信息

2. 使用ActionFilter

在《【asp.net core 系列】9 实战之 UnitOfWork以及自定义代码生成》也就是上一篇中,介绍到了ActionFilter与普通特性类一致,可以通过标注控制器然后启用该ActionFilter。

因为大多数情况下,一个ActionFilter并不会仅仅局限于一个控制器,而是应用于多个控制器。所以这时候,我们通常会设置一个基础控制器,在这个控制器上进行标注,然后让子类继承这个控制器。通过这种方式来实现一次声明多次使用。

当然,在asp.net core 中添加了另外的一种使用ActionFilter的方式,Setup.cs中

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
}

默认是这样的,我们可以通过设置参数来添加一个全局应用的Filter,例如说我们上一篇中创建的 UnitOfWorkFilterAttribute:

services.AddControllersWithViews(options=>
{
    options.Filters.Add<UnitOfWorkFilterAttribute>();
});

通过这种方式可以启用一个全局ActionFilter。如果需要使用asp.net core的默认依赖注入可以使用 AddService进行配置。(依赖注入的内容在后续会讲解)。

3. 工具类生成

继续上一篇遗留的内容:

public static void CreateEntityTypeConfig(Type type)
{
    var targetNamespace = type.Namespace.Replace("Data.Models", "");
    if (targetNamespace.StartsWith("."))
    {
        targetNamespace = targetNamespace.Remove(0);
    }
    var targetDir = Path.Combine(new[] { CurrentDirect, "Domain.Implements", "EntityConfigures" }.Concat(
        targetNamespace.Split('.')).ToArray());

    if (!Directory.Exists(targetDir))
    {
        Directory.CreateDirectory(targetDir);
    }
    var baseName = type.Name.Replace("Entity", "");
    if (!string.IsNullOrEmpty(targetNamespace))
    {
        targetNamespace = $".{targetNamespace}";
    }

    var file = $"using {type.Namespace};" +
        $"\r\nusing Microsoft.EntityFrameworkCore;" +
        $"\r\nusing Microsoft.EntityFrameworkCore.Metadata.Builders;" +
        $"\r\nnamespace Domain.Implements.EntityConfigures{targetNamespace}" +
        "\r\n{" +
        $"\r\n\tpublic class {baseName}Config : IEntityTypeConfiguration<{type.Name}>" +
        "\r\n\t{" +
        "\r\n\t\tpublic void Configure(EntityTypeBuilder<SysUser> builder)" +
        "\r\n\t\t{" +
        $"\r\n\t\t\tbuilder.ToTable(\"{baseName}\");" +
        $"\r\n\t\t\tbuilder.HasKey(p => p.Id);" +
        "\r\n\t\t}\r\n\t}\r\n}";
    File.WriteAllText(Path.Combine(targetDir, $"{baseName}Config.cs"), file);
}

工具类其实本质上就是一次文件写入的方法,本身没什么难度。

不过,这里还有有个小问题,每次调用都会覆盖原有的文件,还有就是这里面有很多可以优化的地方,小伙伴们可以自己试试去优化一下,让代码更好看一点。

4 总结

到目前为止,实战系列也有了几篇,很多小伙伴问我能提供一下源码吗?当然,能呀。不过不是现在,容我留个谜底。当主要框架功能完成之后,我就会给小伙伴们发代码的。

其实也是因为现在还没个完整的,开放给小伙伴们也没啥意义。当然了,跟着一块敲,也是能实现的哈。关键地方的代码都有。

本文分享自微信公众号 - 不做码农的开发者(attachie_1),作者:高先生工作室

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-06-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C# 数据操作系列 - 14 深入探索SqlSugar

    在上一篇中,我们知道了如何使用SqlSugar,但是也只是简单的了解了如何使用,仿佛是套着镣铐行走,这明显不符合一个合格的程序员应有的素养。所以,这一篇我们将对...

    程序员小高
  • 【asp.net core 系列】15 自定义Identity

    在之前的文章中简单介绍了一下asp.net core中的Identity,这篇文章将继续针对Identity进行进一步的展开。

    程序员小高
  • C# 数据操作系列 - 3. ADO.NET 离线查询

    在上一篇中,我故意留下了查询的示范没讲。虽然说可以通过以下代码获取一个DataReader:

    程序员小高
  • .NET Core 使用 EF 出错的解决方法

    在.NET Core 项目钟(类库),使用Entity Framework,建立模型生成数据库时,失败

    痴者工良
  • C# 数据操作系列 - 14 深入探索SqlSugar

    在上一篇中,我们知道了如何使用SqlSugar,但是也只是简单的了解了如何使用,仿佛是套着镣铐行走,这明显不符合一个合格的程序员应有的素养。所以,这一篇我们将对...

    程序员小高
  • java安全编码指南之:Mutability可变性

    mutable(可变)和immutable(不可变)对象是我们在java程序编写的过程中经常会使用到的。

    程序那些事
  • 为什么 spring 中,不支持 autowired 静态变量?

    因为静态变量是属于本身类的信息,当类加载器加载静态变量时,Spring 的上下文环境还没有被加载,所以不可能为静态变量绑定值。

    水货程序员
  • 浅谈.Net反射 4

    System.Reflection命名空间下的Assembly类型,代表了一个程序集,并包含了关于程序集的信息。

    小蜜蜂
  • 推荐一个代码自动完成的工具AutoCode

    本文转载:http://www.cnblogs.com/xiaoxiangfeizi/archive/2012/07/24/2605884.html

    跟着阿笨一起玩NET
  • python基础:集合-set()

    py3study

扫码关注云+社区

领取腾讯云代金券