asp.net core 2.2 中的過濾器/篩選器(上)


ASP.NET Core中的過濾器/篩選器

通過使用 ASP.NET Core MVC 中的篩選器,可在請求處理管道中的特定階段之前或之后運行代碼。

注意:本主題不適用於 Razor 頁面。 ASP.NET Core 2.1 及更高版本支持適用於 Razor 頁面的 IPageFilter 和 IAsyncPageFilter。 有關詳細信息,請參閱 Razor 頁面的篩選方法

內置篩選器處理一些任務,例如:

  • 授權(防止用戶訪問未獲授權的資源)。
  • 確保所有請求都使用 HTTPS。
  • 響應緩存(對請求管道進行短路出路,以便返回緩存的響應)。

可以創建自定義篩選器,用於處理橫切關注點。過濾器可以避免在action中編寫一些重復性的代碼。比如異常過濾器可以合並處理異常。

篩選器的工作原理

篩選器在 MVC 操作調用管道(有時稱為篩選器管道)內運行。 篩選器管道在 MVC 選擇了要執行的操作(controller中的action方法)之后運行。

篩選器類型

每種篩選器類型都在篩選器管道中的不同階段(上圖中的mvc action invocation pipeline)執行。

  • 授權篩選器最先運行,用於確定是否已針對當前請求為當前用戶授權。 如果請求未獲授權,它們可以讓管道短路。

//startup:
public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(action =>
            {
                
                action.Filters.Add<AuthorizationFilter>();
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

//authorizationfilter:
 public class AuthorizationFilter : IAsyncAuthorizationFilter
    {
        private readonly ILoggerFactory loggerFactory;

        public AuthorizationFilter(ILoggerFactory loggerFactory)
        {
            this.loggerFactory = loggerFactory;
        }
        public Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            var logger = loggerFactory.CreateLogger<AuthorizationFilter>();
            logger.LogWarning($"authorization filter is executing now ,target action is :{context.ActionDescriptor.DisplayName}");
            return Task.CompletedTask;
        }
    }

 

 

  • 資源篩選器是授權后最先處理請求的篩選器。 它們可以在篩選器管道的其余階段運行之前以及管道的其余階段完成之后運行代碼。 出於性能方面的考慮,可以使用它們來實現緩存或以其他方式讓篩選器管道短路。 它們在模型綁定之前運行,所以可以影響模型綁定。

//startup:
 public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(action =>
            {
                action.Filters.Add<AuthorizationFilter>();
                action.Filters.Add<ResourceFilter>();
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }
//resourcefilter:
 public class ResourceFilter : IAsyncResourceFilter
    {
        private readonly ILoggerFactory loggerFactory;

        public ResourceFilter(ILoggerFactory loggerFactory)
        {
            this.loggerFactory = loggerFactory;
        }
        public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
        {
            var logger = loggerFactory.CreateLogger<ResourceFilter>();
            logger.LogWarning($"resource filter is executing now,valueproviderfactories count:{context.ValueProviderFactories.Count}");
            var executedContext = await next();
            logger.LogWarning($"resource filter is executed now ,result's type is {executedContext.Result.GetType().Name}");

        }
    }

  • 操作篩選器可以在調用單個操作方法之前和之后立即運行代碼。 它們可用於處理傳入某個操作的參數以及從該操作返回的結果。

//startup:
public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(action =>
            {
                action.Filters.Add<ActionsFilter>();
                action.Filters.Add<AuthorizationFilter>();
                action.Filters.Add<ResourceFilter>();
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }
//actionsfilter
public class ActionsFilter : IAsyncActionFilter
    {
        private readonly ILoggerFactory factory;

        public ActionsFilter(ILoggerFactory factory)
        {
            this.factory = factory;
        }
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            var logger = factory.CreateLogger<ActionsFilter>();
            logger.LogWarning($"action filter is executing new ,context.modelstate:{context.ModelState.IsValid}");
            var executedContext = await next();
            logger.LogWarning($"action filter is executed now,executedContext controller:{executedContext.Controller.ToString()}");
        }
    }

  • 異常篩選器用於在向響應正文寫入任何內容之前,對未經處理的異常應用全局策略。

 //startup:
public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(action =>
            {
                action.Filters.Add<ActionsFilter>();
                action.Filters.Add<AuthorizationFilter>();
                action.Filters.Add<ResourceFilter>();
                action.Filters.Add<ExceptionsFilter>();
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }
//exceptionsfilter:
 public class ExceptionsFilter : IAsyncExceptionFilter
    {
        private readonly ILoggerFactory loggerFactory;

        public ExceptionsFilter(ILoggerFactory loggerFactory)
        {
            this.loggerFactory = loggerFactory;
        }
        public Task OnExceptionAsync(ExceptionContext context)
        {
            var logger = loggerFactory.CreateLogger<ExceptionsFilter>();
            logger.LogWarning($"some exception's happened,exception's message:{context.Exception.Message}");
context.Result=new ObjectResult(context.Exception.Message);//這個異常被處理了一下,以200正常返回。
return Task.CompletedTask; } }

上面的日志打印結果可以看出exception filter實在authorization filter和resource filter以及action filter之后執行的它能捕獲的異常是在action執行過程中發生的異常。所以,如果在authorization filter或者resource filter中發生異常的話,它是沒有辦法捕獲的,可以做一個測驗:將authorization filter中拋出一個異常:

可以看到這個異常是被直接拋出來了,並沒有被exception handler中進行處理。接着改在resource filter中拋出一個異常,看看:

同樣,在resource filter中拋出的異常exception filter也是處理不了的。也就是說在篩選器管道中,處於exception篩選器執行之前而執行的代碼拋出的異常,exception篩選器是處理不了的。要想捕獲程序的全局異常,我覺得應該在中間件中定義對異常的捕獲。這個結論還沒有進行證實,有時間再討論。

  • 結果篩選器可以在執行單個操作結果之前和之后立即運行代碼。 僅當操作方法成功執行時,它們才會運行。 對於必須圍繞視圖或格式化程序的執行的邏輯,它們很有用。

//startup:
 public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(action =>
            {
                action.Filters.Add<ActionsFilter>();
                action.Filters.Add<AuthorizationFilter>();
                action.Filters.Add<ResourceFilter>();
                action.Filters.Add<ExceptionsFilter>();
                action.Filters.Add<ResultFilter>();
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }
//resultfilter:
 public class ResultFilter : IAsyncResultFilter
    {
        private readonly ILoggerFactory loggerFactory;

        public ResultFilter(ILoggerFactory loggerFactory)
        {
            this.loggerFactory = loggerFactory;
        }
        public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
        {
            var logger = loggerFactory.CreateLogger<ResultFilter>();
            logger.LogWarning($"result filter is executing, context.result is :{context.Result.GetType().Name}");
            var executedContext = await next();
            logger.LogWarning($"result filter is executed ,context.result is {executedContext.Result.GetType().Name}");
        }
    }

上面的結果是action中沒有拋出異常,正常執行的結果,如果在action中拋出異常:

上下對比一下會發現,result filter只會在action正常執行沒有拋出異常之后才會執行。exception filter是會捕獲action拋出的異常。

下圖展示了這些篩選器類型在篩選器管道中的交互方式。

實現

通過不同的接口定義,篩選器同時支持同步和異步實現。例如我上面舉的例子全部都是用異步的方式實現的。

可在其管道階段之前和之后運行代碼的同步篩選器定義 OnStageExecuting 方法和 OnStageExecuted 方法。 例如,在調用操作方法之前調用 OnActionExecuting,在操作方法返回之后調用 OnActionExecuted

 

using FiltersSample.Helper;
using Microsoft.AspNetCore.Mvc.Filters;

namespace FiltersSample.Filters
{
    public class SampleActionFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context)
        {
            // do something before the action executes
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            // do something after the action executes
        }
    }
}

異步篩選器定義單一的 OnStageExecutionAsync 方法。 此方法采用 FilterTypeExecutionDelegate 委托來執行篩選器的管道階段。 例如,ActionExecutionDelegate 調用該操作方法(如果沒有下一個action filter)或下一個操作篩選器(下一個action filter),用戶可以在調用它之前和之后執行代碼。

可以在單個類中為多個篩選器階段實現接口。 例如,ActionFilterAttribute 類實現 IActionFilter 和 IResultFilter,以及它們的異步等效接口。

 

注意:同步和異步的只需要實現一個就行,如果兩個都實現了,會優先執行異步版本的。

篇幅太長,再來一片吧。


免責聲明!

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



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