.netcore之Filter


.netcore中的Filter是什么

過濾器是指在netcore的請求處理管道中運行指定的代碼邏輯。例如指定身份驗證,異常處理、盜鏈驗證等等。過濾器其實是實現了AOP,我們可以通過過濾器實現額外的附加操作而不會影響實際的業務邏輯實現。

.netcore中的過濾器主要有以下幾種

  1. Authorization Filter  ----授權認證過濾器
  2. Resource Filter       ----資源過濾器
  3. Action Filter       ----Action過濾器
  4. Exception Filter      ----異常過濾器
  5. Result Filter           ----結果過濾器

Filter的執行順序

 

 

 

各類Filter的實現示例

1、Authorization Filter:一般用於鑒權認證。如果認證不通過則直接返回(不過現在很多情況下都是使用鑒權中心進行驗證的(例如Id4實現的用戶中心),很少再需要實現Authorization Filter了,當然也有特例,這要根據實際業務而定)。

示例

 1     /// <summary>
 2     /// 自定義鑒權認證過濾器
 3     /// </summary>
 4     public class CustomerAuthorizationFilterAttribute : Attribute, IAsyncAuthorizationFilter,IOrderedFilter
 5     {
 6         public int Order { get; set; } = 0;
 7 
 8         /// <summary>
 9         /// 
10         /// </summary>
11         /// <param name="context"></param>
12         /// <returns></returns>
13         public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
14         {
15             var authFlag = false;
16             var descriptor = context.ActionDescriptor as ControllerActionDescriptor;
17             if (descriptor != null)
18             {
19                 authFlag = descriptor.ControllerTypeInfo.GetCustomAttributes(true).OfType<DescriptionAttribute>().Any()
20                 || descriptor.MethodInfo.GetCustomAttributes(true).OfType<DescriptionAttribute>().Any();
21             }
22             if (authFlag)
23             {
24                 string userId = context.HttpContext.User.Claims.FirstOrDefault(p => p.Type == "Subject")?.Value;
25                 ISimpleUserService simpleUserService = context.HttpContext.RequestServices.GetService<ISimpleUserService>();
26                 var checkAuthResult = await CheckAuth(userId, simpleUserService);
27                 if (checkAuthResult == false)
28                 {
29                     var responseModel = new ApiResponseModel<object>()
30                     {
31                         Code = "E000999",  //自定義錯誤編碼
32                         Message = "NO PERMISSIONS",
33                     };
34                     context.Result = new OkObjectResult(responseModel);   //根據實際業務定義返回結果,可以是OkObjectResult,也可以是其他(例如UnauthorizedObjectResult)
35                     //context.Result = new UnauthorizedObjectResult(responseModel);
36                     return;
37                 }
38             }
39 
40         }
41         /// <summary>
42         /// 鑒權
43         /// </summary>
44         /// <param name="userId"></param>
45         /// <param name="userService"></param>
46         /// <returns></returns>
47         private async Task<bool> CheckAuth(string userId, ISimpleUserService userService)
48         {
49             //TODO Check authorization //var user= await userService.GetUserBydIdAsync(userId);
50             await Task.Delay(0);
51             return true;
52         }
53     }

 

 

2、ResourceFilter:一般用於對資源型信息進行過濾。常用於防盜鏈/資源緩存(根據判斷緩存中是否存在當前資源,存在則直接返回)。

它分為執行前,執行后的實現。

執行前(OnResourceExecuting):在Authorization Filter執行完成后。

執行后(OnResourceExecuted):在Result Execution之后

示例:

 1     /// <summary>
 2     /// 自定義Resource Filter
 3     /// </summary>
 4     public class CustomerResourceFilter : Attribute, IResourceFilter
 5     {
 6         /// <summary>
 7         /// 執行后
 8         /// </summary>
 9         /// <param name="context"></param>
10         public void OnResourceExecuted(ResourceExecutedContext context)
11         {
12             //無

13 } 14 /// <summary> 15 /// 執行前 16 /// </summary> 17 /// <param name="context"></param> 18 public void OnResourceExecuting(ResourceExecutingContext context) 19 { 20 #region 防盜鏈 --特別是一些文件,圖片信息時 21 string urlReferrer = context.HttpContext.Request.Headers["Referer"]; 22 if (string.IsNullOrWhiteSpace(urlReferrer))//直接訪問 23 { 24 context.Result = new ForbidResult(); 25 } 26 else if (!urlReferrer.Contains("localhost"))//非當前域名 27 { 28 context.Result = new ForbidResult(); 29 } 30 #endregion 31 #region 緩存信息 32 //從緩存讀取信息 33 IMemoryCache cache = context.HttpContext.RequestServices.GetService<IMemoryCache>(); 34 //請求路徑作為緩存的Key 35 string path = context.HttpContext.Request.Path.ToString(); 36 object value = null; 37 if (cache.TryGetValue(path, out value)) 38 { 39 string result = value.ToString(); 40 //如果有Result,則不會往執行后面的過濾器 41 context.Result = new ContentResult() { Content = result }; 42 } 43 #endregion 44 } 45 }

 3、Action Filter :執行過濾器,一般是在Action執行前后進行過濾。可以通過這個Filter進行一些參數驗證邏輯,也可以通過這個filter記錄操作日志信息。

它分為執行前,執行后的實現。

執行前(OnActionExecuting):在Action執行之前。

執行后(OnActionExecuted):在Action執行完成之后。

需要注意的是:ActionFilter are not supported in Razor Pages.(Action Filter 不支持Razor Pages)

示例:

 1  /// <summary>
 2     /// 自定義Action Filter
 3     /// </summary>
 4     public class CustomerActionFilter : Attribute, IActionFilter
 5     {
 6         private readonly ILogger _logger;
 7         public CustomerActionFilter(ILogger<CustomerActionFilter> logger)
 8         {
 9             this._logger = logger;
10         }
11         /// <summary>
12         /// 執行后
13         /// </summary>
14         /// <param name="context"></param>
15         public void OnActionExecuted(ActionExecutedContext context)
16         {
17             string userId = context.HttpContext.User.Claims.FirstOrDefault(p => p.Type == "Subject")?.Value;
18           
19             var descriptor = context.ActionDescriptor as ControllerActionDescriptor;
20             if (descriptor != null)
21             {
22                 string message = $"{descriptor.ControllerName}--{descriptor.ActionName} excecuted by {userId} at {DateTime.UtcNow.ToLongDateString()}";
23                 _logger.LogInformation(message);
24 
25             }
26         }
27         /// <summary>
28         /// 執行前
29         /// </summary>
30         /// <param name="context"></param>
31         public void OnActionExecuting(ActionExecutingContext context)
32         {
33             string userId = context.HttpContext.User.Claims.FirstOrDefault(p => p.Type == "Subject")?.Value;
34            
35             var descriptor = context.ActionDescriptor as ControllerActionDescriptor;
36             if (descriptor != null)
37             {
38                 string message = $"{descriptor.ControllerName}--{descriptor.ActionName} excecuting by {userId} at {DateTime.UtcNow.ToLongDateString()}";
39                 _logger.LogInformation(message);
40 
41             }
42         }
43     }

 

4、Exception Filter :異常過濾器,用於捕獲未處理的異常信息。

示例:

 1     /// <summary>
 2     /// global exception filter
 3     /// </summary>
 4     public class GlobalExceptionFilter : Attribute, IAsyncExceptionFilter
 5     {
 6         private readonly ILogger _logger;
 7         /// <summary>
 8         /// 
 9         /// </summary>
10         /// <param name="logger"></param>
11         public GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger)
12         {
13             this._logger = logger;
14         }
15         /// <summary>
16         /// 
17         /// </summary>
18         /// <param name="context"></param>
19         /// <returns></returns>
20         public async Task OnExceptionAsync(ExceptionContext context)
21         {
22             this._logger.LogError($"在響應 {context.HttpContext.Request.Path} 時出現異常,信息:{context.Exception.Message}");
23 
24             if (!context.ExceptionHandled)        //異常未被處理
25             {
26                 //if (this.IsAjaxRequest(context.HttpContext.Request))//ajax請求
27                 //{
28                     var result= new ApiResponseModel<object>()
29                     {
30                         Code = "E9999999",
31                         Message = "系統發生錯誤,請聯系管理員",
32 
33                     };
34                     context.Result = new BadRequestObjectResult(result);//中斷---請求到這里結束了,不在往下執行Action
35                 //}
36                 //else
37                 //{
38                 //    var result = new ViewResult { ViewName = "~/Views/Shared/Error.cshtml" };
39                 //    result.ViewData = new ViewDataDictionary(_modelMetadataProvider,
40                 //                                                context.ModelState);
41                 //    result.ViewData.Add("Exception", context.Exception.Message);
42                 //    context.Result = result;
43                 //}
44                 context.ExceptionHandled = true;//異常已被處理
45             }
46         }
47         private bool IsAjaxRequest(HttpRequest request)
48         {
49             string header = request.Headers["X-Requested-With"];
50             return "XMLHttpRequest".Equals(header);
51         }
52     }

5、Result Filter: 結果過濾器,例如可以對結果進行格式化、大小寫轉換,附件特定返回信息等操作。(格式化其實可以通過在startup中指定,ResultFilter較少使用)

示例

 1     /// <summary>
 2     /// 自定義ResultFilter
 3     /// </summary>
 4     public class CustomerResultFilter : Attribute, IResultFilter
 5     { 
 6 
 7         public void OnResultExecuting(ResultExecutingContext context)
 8         {
 9             var headerName = "NextToken";
10             context.HttpContext.Response.Headers.Add(
11                 headerName, new string[] { "xxxxxxx" }); 
12             //TODO如果訪問過於頻繁返回Too Many Request
13         }
14 
15         public void OnResultExecuted(ResultExecutedContext context)
16         {
17             //
18         }
19 
20     }

 

Filter的注冊

1、特性標識

1-1、指定對應的Filter特性(可以是Controller上標注也可以是指定的Action)

例如

1     [Route("api/[controller]")]
2     [ApiController]
3     [CustomerAuthorizationFilter]
4     public class ProductController : ControllerBase
5     {
6     .....
7     }

 

1-2、ServiceFilter和TypeFilter

為什么會有ServcieFilter和TypeFilter來指定呢?因為Filter可能存在有參的構造函數(它的參數可能是由netcore的DI框架注入的或者其它的,此時就不能像1-1中通過Filter屬性直接標識了,只能通過ServiceFilter或者TypeFilter進行標注)。它們之間的區別主要是:

  • ServiceFilter檢索來自DI過濾器的一個實例。使用ServiceFilter而不注冊過濾器類型會導致異常(即必須在startup中添加對應Filter的服務)。
  • TypeFilterAttribute非常類似於ServiceFilterAttribute(和還實現IFilterFactory),但其類型不從DI容器直接解決。它使用的是Microsoft.Extensions.DependencyInjection.ObjectFactory實例化類型(對應的Filter無需注冊到DI中)。

2、全局注冊

在startup--ConfigureServices(IServiceCollection services)中注冊

1             services.AddMvc(options =>
2             {
3                 options.Filters.Add<CustomerActionFilter>();
4                 options.Filters.Add<CustomerAuthorizationFilterAttribute>();
5             });

 

------------------------------------------

參考

https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-5.0

 

 

------

本文地址:https://www.cnblogs.com/johnyong/p/14408721.html

 


免責聲明!

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



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