asp.net core系列 68 Filter管道過濾器


一.概述

  本篇詳細了解一下asp.net core filters,filter叫"篩選器"也叫"過濾器",是請求處理管道中的特定階段之前或之后運行代碼。filter用於處理橫切關注點。 橫切關注點的示例包括:錯誤處理、緩存、配置、授權和日志記錄。 filter可以避免重復代碼,通過Attribute特性來實現filter過濾。Filter適應於 Razor Pages,  API controllers,  mvc controllers。filter基類是IFilterMetadata 接口,該接口只是用來標記是一個filter過濾器。

  前段時間在項目中實現了IAsyncAuthorizationFilter接口對用戶訪問controller或action進行了授權,在OnAuthorizationAsync方法中使用context.Result可使管道短道。IAsyncAuthorizationFilter是屬於授權過濾器中的一種。勿在授權過濾器內拋出異常,這是因為所拋出的異常不會被處理。下面是簡要的實現授權代碼:

   [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class PermissionFilter : Attribute,IAsyncAuthorizationFilter
  {
    public   Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
                IIdentity user = context.HttpContext.User.Identity;
                if (!user.IsAuthenticated)
                {
            //跳轉到登錄頁
              context.Result = new LocalRedirectResult(url);
             return Task.CompletedTask;
             }
 
         //根據當前用戶,判斷當前訪問的action,沒有權限時返回403錯誤
         context.Result = new ForbidResult();
            
             return Task.CompletedTask;
     }
    }

  在官方文檔中說到:"自定義授權篩選器Filter需要自定義授權框架, 建議配置授權策略或編寫自定義授權策略,而不是編寫自定義Filter篩選器"。

  這里的意思是說,如果在項目中不使用asp.net core自帶的identity,那么需要自定義授權框架,也就是需要自己建立一套用戶權限表。 如果使用了identity建議配置授權策略或編寫自定義授權策略。如果不用identity自己建立用戶權限表,那么就可以編寫自定義Filter篩選器。我在項目開發中是自己建立了用戶權限表沒有用identity。因為使用identity表還需要擴展字段,而且與EF core結合緊密,如果不使用EF core需要重寫訪問identity表的實現。

  下面是identity與ef core相關的代碼: 

   services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

   -- TContext必須是DbContext類型
    public static IdentityBuilder AddEntityFrameworkStores<TContext>(this IdentityBuilder builder) where TContext : DbContext

 

二.Filter 篩選器類型

    Authorization filters  授權篩選器

    Resource filters       資源篩選器

    Action filters            操作篩選器 (Razor Pages 中使用 IPageFilter 和 IAsyncPageFilter)

    Exception filters      異常篩選器

    Result filters            結果篩選器

    每種Filter 類型都在Filter 管道中的不同階段執行,當用戶請求進來時,經過Filter管道,執行Filter的階段順序如下所示:

    上面每種Filter 類型都有自己的接口,都同時支持同步和異步實現,上面的授權示例就是一個異步實現授權篩選器IAsyncAuthorizationFilter。Filter 接口或直接或間接實現了IFilterMetadata接口,IFilterMetadata接口只是用來標記是一個Filter 。

 

三.filter篩選器作用域

  (1) 將 attribute特性應用在 action上。

  (2) 將 attribute特性應用在 controller上。

  (3) 全局篩選器應用在controller和action上

    下面使用全局篩選器,使用MvcOptions.Filters 集合,添加三個篩選器,如下面的代碼所示:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.Filters.Add(new AddHeaderAttribute("GlobalAddHeader",
            "Result filter added to MvcOptions.Filters"));         // An instance
        options.Filters.Add(typeof(MySampleActionFilter));         // By type
        options.Filters.Add(new SampleGlobalActionFilter());       // An instance
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
//實現了內置篩選器屬性
public class AddHeaderAttribute : ResultFilterAttribute
//實現了操作篩選器
public class MySampleActionFilter : IActionFilter
//實現了操作篩選器
public class SampleGlobalActionFilter : IActionFilter

 

四.內置篩選器Attribute屬性

    下面都是filter內置的屬性類,需要去實現這些抽象屬性類。

      ActionFilterAttribute
      ExceptionFilterAttribute
      ResultFilterAttribute
      FormatFilterAttribute
      ServiceFilterAttribute
      TypeFilterAttribute

  下面一個示例是為響應添加標頭,開發人員來實現ResultFilterAttribute抽象類:

public class AddHeaderAttribute : ResultFilterAttribute
{
    private readonly string _name;
    private readonly string _value;

    public AddHeaderAttribute(string name, string value)
    {
        _name = name;
        _value = value;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add( _name, new string[] { _value });
        base.OnResultExecuting(context);
    }
}

     通過使用屬性,篩選器可接收參數,將 AddHeaderAttribute 添加到控制器或操作方法,並指定 HTTP 標頭的名稱和值,如下所示:

        [AddHeader("Author", "Steve Smith @ardalis")]
        public IActionResult Hello(string name)
        {
            return Content($"Hello {name}");
        }
    

 

五.Filter三種依賴關系注入

    (1)ServiceFilterAttribute
    (2)TypeFilterAttribute
    (3)在屬性上實現 IFilterFactory。

  5.1 ServiceFilterAttribute演示

    下面是在 ConfigureServices 中注冊服務篩選器實現類型,內置的ServiceFilterAttribute會從DI 檢索篩選器實例。

public class AddHeaderResultServiceFilter : IResultFilter
{
    private ILogger _logger;
    public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var headerName = "OnResultExecuting";
        context.HttpContext.Response.Headers.Add(
            headerName, new string[] { "ResultExecutingSuccessfully" });
        _logger.LogInformation($"Header added: {headerName}");
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Can't add to headers here because response has started.
    }
}

     在以下代碼中,AddHeaderResultServiceFilter 將添加到 DI 容器中:

    services.AddScoped<AddHeaderResultServiceFilter>();

    在以下代碼中,通過ServiceFilter 屬性,將從 DI 中檢索 AddHeaderResultServiceFilter 篩選器的實例:

[ServiceFilter(typeof(AddHeaderResultServiceFilter))]
public IActionResult Index()
{
    return View();
}

 

六. 各種篩選器介紹

  6.1 Authorization filters 

    是篩選器管道中第一個運行的篩選器。是控制對Action方法的訪問。

     在Action之前執行的方法,沒有在Action之后執行的方法。

       注意:不要在授權篩選器中引發異常

    場景:

      如果使用identity,可用配置授權策略或編寫自定義授權策略。詳情查看identity部分。

      如果不使用identity,可編寫自定義篩選器。

 

  6.2 Resource filters

    實現 IResourceFilter 或 IAsyncResourceFilter 接口。

    它執行會覆蓋篩選器管道的絕大部分。

    它在授權篩選器之后運行。

    場景:

      可以防止模型綁定訪問表單數據。

      用於上傳大型文件,以防止表單數據被讀入內存。

 

  6.3 Action filters

    實現 IActionFilter 或 IAsyncActionFilter 接口。

    它圍繞着Action方法的執行。

    它方法中有個重要屬性ActionArguments ,用於讀取action的輸入參數。

    場景:

      可以在該接口的OnActionExecuting方法中進行驗證模型狀態(ModelState.IsValid),如果狀態無效,則返回錯誤(這個場景很有用)。

 

  6.4 Exception filters

    實現 IExceptionFilter 或 IAsyncExceptionFilter。

    它沒有之前和之后的事件,

    可實現該接口的 OnException 或 OnExceptionAsync方法。

    處理 Razor 頁面或控制器創建、模型綁定、操作篩選器或操作方法中發生的未經處理的異常

    若要處理異常(不再throw),請將 ExceptionHandled 屬性設置為 true。

    場景:

      實現常見的錯誤處理策略。

      非常適合捕獲發生在action中的異常。

    下面是項目中的一個示例, 注意在授權器中引發的異常是無法捕獲的。 

    public class GlobalAsyncExceptionFilter : IAsyncExceptionFilter
    {
        //日志對象
        public readonly ILoggerEX _logger;

        public GlobalAsyncExceptionFilter(ILoggerEX logger)
        {
            _logger = logger;
        }

        public Task OnExceptionAsync(ExceptionContext context)
        {
            string errormsg = "全局捕獲未處理的異常,errorMsg:" + context.Exception.Message + context.Exception.StackTrace;
            _logger.Error(errormsg);
            context.Result = new ObjectResult(errormsg);
       context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
//不再throw,在此捕獲 context.ExceptionHandled = true; return Task.CompletedTask; } }
            services.AddMvc(option=> {
                option.Filters.Add(typeof(GlobalAsyncExceptionFilter));
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        public CustomerController(INotificationHandler<DomainNotification> notifications,
                                  ILoggerEX logger,
                                  CustomerAppService customerAppService)
            : base(notifications)
        {
            _customerAppService = customerAppService;
            _logger = logger;
            throw new Exception("自定義一個未捕獲的異常!");
        }

    當訪問Customer控制器時,全局異常過濾器將會自動捕獲,如下所示:

 

  6.5 Result filters

    實現 IResultFilter 或 IAsyncResultFilter 或 IAlwaysRunResultFilter 或 IAsyncAlwaysRunResultFilter

    它執行圍繞着action結果的執行。當異常篩選器處理異常時,不執行結果篩選器

    如果在 IResultFilter.OnResultExecuting 中引發異常,則會導致:

      (1) 阻止action結果和后續篩選器的執行。

      (2) 結果被視為失敗。

 

 

  更詳細的資料參考官網文檔,后續在實際項目中應用到的篩選器,將會再本篇繼續補上。

  參考資料:

    官方文檔

    官方示例代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM