Talk about Asp. net filter Filter

  • 2021-11-13 07:16:06
  • OfStack

Recently, when sorting out and optimizing. net code, I found several unfriendly processing phenomena: login judgment, permission authentication, logging, exception handling and other common operations, which are everywhere in action in the project. In code optimization, this 1 point is a very important focus. At this time, the filter and interceptor (Filter) in net will come in handy. Now, according to the actual work in the past few days, I have made a simple comb and shared it for everyone's reference and communication. If there is anything wrong with writing, I will point out and communicate more.

Overview:

Filter in. net mainly includes the following four categories: Authorize (authorization), ActionFilter (custom) and HandleError (error handling).

过滤器

类名

实现接口

描述

授权

AuthorizeAttribute

IAuthorizationFilter

此类型(或过滤器)用于限制进入控制器或控制器的某个行为方法,比如:登录、权限、访问控制等等

异常

HandleErrorAttribute

IExceptionFilter

用于指定1个行为,这个被指定的行为处理某个行为方法或某个控制器里面抛出的异常,比如:全局异常统1处理。

自定义

ActionFilterAttribute

IActionFilter和IResultFilter

用于进入行为之前或之后的处理或返回结果的之前或之后的处理,比如:用户请求日志详情日志记录

AuthorizeAttribute: Authentication and Authorization

Authentication authorization is mainly to authenticate the first entrance of all action access, and to do the first supervision filter interception gate for user access.

Implementation: It is necessary to customize a class, inherit AuthorizeAttribute and rewrite OnAuthorization, and all Request information requested by users can be obtained in OnAuthorization. In fact, all the data support of all authentication interception operations we do comes from Request.

Specific verification process design:

IP White List: This is mainly for API to do IP restriction, only specified IP can be accessed, and non-specified IP can be returned directly

Request frequency control: This is mainly to control the user's access frequency, mainly for API, beyond the request frequency to return directly.

Login authentication: Login authentication 1 is verified by passing token in the requested header, so that MVC login authentication with 1 is used, and Auth authentication with API interface is also used, and it does not depend on the user's front-end js settings.

Authorization authentication: Authorization authentication is simple, mainly to verify whether the user has the authority, if not, directly do the corresponding return processing.

Similarities and differences between MVC and API:

Namespace: MVC: System. Web. Http. Filters; API: System. Web. Mvc

Injection mode: In injection mode, it mainly includes: global- > Controller Controller- > Behavior Action

Global registration: All Aciton for all systems use

Controller: Only works for Action under this Controller

Action: Only works for this Action

Among them, there are one difference between MVC and API for global registration:

MVC is injected into FilterConfig. cs


filters.Add(new XYHMVCAuthorizeAttribute());

API is injected into WebApiConfig. cs


config.Filters.Add(new XYHAPIAuthorizeAttribute());

Note: In actual use, for authentication authorization, we generally add global authentication, but some action does not need authentication, such as the original login Action, etc., so how to exclude it? In fact, it is also very simple. We only need to define an Attribute integrated Attribute or an AllowAnonymousAttribute of the system by ourselves. In the action that does not need verification, we only need to register the corresponding Attribute and do a filter before verification, such as:


//  Have  AllowAnonymous  The interface of the property directly turns on the green light 
   if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any())
   {
    return;
   }

API AuthFilterAttribute instance code


/// <summary>
 ///  Authorization authentication filter 
 /// </summary>
 public class XYHAPIAuthFilterAttribute : AuthorizationFilterAttribute
 {
  /// <summary>
  ///  Authentication authorization verification 
  /// </summary>
  /// <param name="actionContext"> Request context </param>
  public override void OnAuthorization(HttpActionContext actionContext)
  {
   //  Have  AllowAnonymous  The interface of the property directly turns on the green light 
   if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any())
   {
    return;
   }

   //  Do it before request 1 Layer interception, main verification token Validity and verification of 
   HttpRequest httpRequest = HttpContext.Current.Request;

   //  Get apikey
   var apikey = httpRequest.QueryString["apikey"];

   //  Do it first IP Whitelist check  
   MBaseResult<string> result = new AuthCheckService().CheckIpWhitelist(FilterAttributeHelp.GetIPAddress(actionContext.Request), apikey);

   //  Inspection time rub 
   string timestamp = httpRequest.QueryString["Timestamp"];
   if (result.Code == MResultCodeEnum.successCode)
   {
    //  Inspection time rub  
    result = new AuthCheckService().CheckTimestamp(timestamp);
   }

   if (result.Code == MResultCodeEnum.successCode)
   {
    //  Do request frequency verification  
    string acitonName = actionContext.ActionDescriptor.ActionName;
    string controllerName = actionContext.ActionDescriptor.ControllerDescriptor.ControllerName;
    result = new AuthCheckService().CheckRequestFrequency(apikey, $"api/{controllerName.ToLower()}/{acitonName.ToLower()}");
   }

   if (result.Code == MResultCodeEnum.successCode)
   {
    //  Signature verification 

    //  Get all request parameters 
    Dictionary<string, string> queryParameters = httpRequest.GetAllQueryParameters();

    result = new AuthCheckService().SignCheck(queryParameters, apikey);

    if (result.Code == MResultCodeEnum.successCode)
    {
     //  If there is NoChekokenFilterAttribute  Label   Then don't do it directly token Certification 
     if (actionContext.ActionDescriptor.GetCustomAttributes<XYHAPINoChekokenFilterAttribute>().Any())
     {
      return;
     }

     //  Calibration token Validity of 
     //  Get 1 A  token
     string token = httpRequest.Headers.GetValues("Token") == null ? string.Empty :
      httpRequest.Headers.GetValues("Token")[0];

     result = new AuthCheckService().CheckToken(token, apikey, httpRequest.FilePath);
    }
   }

   //  Output 
   if (result.Code != MResultCodeEnum.successCode)
   {
    // 1 Be sure to instantiate 1 A response, Will it eventually be executed action Code in 
    actionContext.Response = new HttpResponseMessage(HttpStatusCode.OK);
    // You need to specify your own output content and type 
    HttpContext.Current.Response.ContentType = "text/html;charset=utf-8";
    HttpContext.Current.Response.Write(JsonConvert.SerializeObject(result));
    HttpContext.Current.Response.End(); //  End the response here, and it will not walk by the system 
   }
  }
 }

MVC AuthFilterAttribute instance code


/// <summary>
 /// MVC自定义授权
 /// 认证授权有两个重写方法
 /// 具体的认证逻辑实现:AuthorizeCore 这个里面写具体的认证逻辑,认证成功返回true,反之返回false
 /// 认证失败处理逻辑:HandleUnauthorizedRequest 前1步返回 false时,就会执行到该方法中
 /// 但是,我平时在应用过程中,1般都是在AuthorizeCore根据不同的认证结果,直接做认证后的逻辑处理
 /// </summary>
 public class XYHMVCAuthorizeAttribute : AuthorizeAttribute
 {
  /// <summary>
  /// 认证逻辑
  /// </summary>
  /// <param name="filterContext">过滤器上下文</param>
  public override void OnAuthorization(AuthorizationContext filterContext)
  {

   // 此处主要写认证授权的相关验证逻辑
   // 该部分的验证1般包括两个部分
   // 登录权限校验
   // --我们的1般处理方式是,通过header中传递1个token来进行逻辑验证
   // --当然不同的系统在设计上也不尽相同,有的也会采用session等方式来验证
   // --所以最终还是根据其项目本身的实际情况来进行对应的逻辑操作

   // 具体的页面权限校验
   // --该部分的验证是具体的到页面权限验证
   // --我看有得小伙伴没有做到这1个程度,直接将这1步放在前端js来验证,这样不是很安全,但是可以拦住小白用户
   // --当然有的系统根本就没有做权限控制,那就更不需要这1个逻辑了。
   // --所以最终还是根据其项目本身的实际情况来进行对应的逻辑操作

   // 现在用1个粗暴的方式来简单模拟实现过,用系统当前时间段秒厨艺3,取余数
   // 当余数为0:认证授权通过
   //   1:代表为登录,调整至登录页面
   //   2:代表无访问权限,调整至无权限提示页面

   // 当然,在这也还可以做1些IP白名单,IP黑名单验证 请求频率验证等等

   // 说到这而,还有1点需要注意,如果我们选择的是全局注册该过滤器,那么如果有的页面根本不需要权限认证,比如登录页面,那么我们可以给不需要权限的认证的控制器或者action添加1个特殊的注解 AllowAnonymous ,来排除

   // 获取Request的几个关键信息
   HttpRequest httpRequest = HttpContext.Current.Request;
   string acitonName = filterContext.ActionDescriptor.ActionName;
   string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;

   // 注意:如果认证不通过,需要设置filterContext.Result的值,否则还是会执行action中的逻辑

   filterContext.Result = null;
   int thisSecond = System.DateTime.Now.Second;
   switch (thisSecond % 3)
   {
    case 0:
     // 认证授权通过
     break;
    case 1:
     // 代表为登录,调整至登录页面
     // 只有设置了Result才会终结操作
     filterContext.Result = new RedirectResult("/html/Login.html");
     break;
    case 2:
     // 代表无访问权限,调整至无权限提示页面
     filterContext.Result = new RedirectResult("/html/NoAuth.html");
     break;
   }
  }
 }

ActionFilter: Custom Filter

Custom filter, mainly monitoring action request before and after, processing results before and after the return of events. API has only two methods before and after the request.

重新方法

方法功能描述

使用于

OnActionExecuting

1个请求在进入到aciton逻辑前执行

MVC、API

OnActionExecuted

1个请求aciton逻辑执行后执行

MVC、API

OnResultExecuting

对应的view视图渲染前执行

MVC

OnResultExecuted

对应的view视图渲染后执行

MVC

Among these methods, we are mainly used to record the interaction log and record the time-consuming situation of each step, so as to optimize the use of the subsequent system. Specific use, according to their own business scenarios.

Among them, the similarities and differences between MVC and API are similar to the similarities and differences of authentication authorization mentioned above, so they are not described in detail.

The following 1 example code:

API Defines Filter Instance DEMO Code


/// <summary>
 /// Action Filter 
 /// </summary>
 public class XYHAPICustomActionFilterAttribute : ActionFilterAttribute
 {
  /// <summary>
  /// Action Start of execution 
  /// </summary>
  /// <param name="actionContext"></param>
  public override void OnActionExecuting(HttpActionContext actionContext)
  {

  }

  /// <summary>
  /// action After execution 
  /// </summary>
  /// <param name="actionContext"></param>
  public override void OnActionExecuted(HttpActionExecutedContext actionContext)
  {
   try
   {
    //  Build 1 Log data model 
    MApiRequestLogs apiRequestLogsM = new MApiRequestLogs();

    // API Name 
    apiRequestLogsM.API = actionContext.Request.RequestUri.AbsolutePath;

    // apiKey
    apiRequestLogsM.API_KEY = HttpContext.Current.Request.QueryString["ApiKey"];

    // IP Address 
    apiRequestLogsM.IP = FilterAttributeHelp.GetIPAddress(actionContext.Request);

    //  Get token
    string token = HttpContext.Current.Request.Headers.GetValues("Token") == null ? string.Empty :
        HttpContext.Current.Request.Headers.GetValues("Token")[0];
    apiRequestLogsM.TOKEN = token;

    // URL
    apiRequestLogsM.URL = actionContext.Request.RequestUri.AbsoluteUri;

    //  Return information 
    var objectContent = actionContext.Response.Content as ObjectContent;
    var returnValue = objectContent.Value;
    apiRequestLogsM.RESPONSE_INFOR = returnValue.ToString();

    //  Because the database can only store the maximum 4000 String, so do the return value 1 Interception 
    if (!string.IsNullOrEmpty(apiRequestLogsM.RESPONSE_INFOR) &&
     apiRequestLogsM.RESPONSE_INFOR.Length > 4000)
    {
     apiRequestLogsM.RESPONSE_INFOR = apiRequestLogsM.RESPONSE_INFOR.Substring(0, 2000);
    }

    //  Request parameter 
    apiRequestLogsM.REQUEST_INFOR = actionContext.Request.RequestUri.Query;

    //  Definition 1 Asynchronous delegates  , Asynchronous logging 
    // Func<MApiRequestLogs, string> action = AddApiRequestLogs;// Declaration 1 Delegation 
    // IAsyncResult ret = action.BeginInvoke(apiRequestLogsM, null, null);

   }
   catch (Exception ex)
   {

   }
  }
 }

HandleError: Error Handling

Exception handling is very common for us, very good use of exception handling, can be very good to avoid the whole try/catch. Exception handling box is very simple, the value needs custom integration: ExceptionFilterAttribute, and custom implementation: OnException method can be.

In OnException, we can do some corresponding logical processing according to our own needs, such as recording exception logs, which is convenient for subsequent problem analysis and follow-up.

OnException also has a very important processing, that is, the exception of the results of the package, return a very friendly results to the user, to avoid some unnecessary information returned to the user. For example, for MVC, follow up different exceptions, adjust the system 1 to a friendly prompt page, and so on; For API, then we can return several encapsulations of one system 1, which is convenient for user system 1 to process the results.

Exception handling instance code for MVC:


 /// <summary>
 /// MVC Custom exception handling mechanism 
 ///  When it comes to exception handling, in fact, the first in our mind 1 The reaction, too, should be try/cache Operation 
 ///  However, in actual development, it is very likely that address errors will not enter at all try In, or not by try Handled to exception 
 ///  This class plays a role, which can well handle uncaught exceptions and do corresponding logical processing 
 ///  Custom exception mechanism, mainly integrating HandleErrorAttribute  Rewrite its OnException Method 
 /// </summary>
 public class XYHMVCHandleError : HandleErrorAttribute
 {
  /// <summary>
  ///  Handle exceptions 
  /// </summary>
  /// <param name="filterContext"> Exception context </param>
  public override void OnException(ExceptionContext filterContext)
  {
   //  In our usual projects, exception handling 1 There are two functions in general 
   // 1: Detailed log of exceptions, which is convenient for post-analysis log 
   // 2: Unification of anomalies 1 Friendly handling, such as redirecting to a friendly prompt page according to the exception type 

   //  Both unprocessed exception information and request information can be obtained in this system 
   //  Here, you can do the corresponding logical processing according to the actual project needs 
   //  Here is a brief list of several key ways to obtain information 

   //  Controller name   Note that what is obtained in this way is 1 Full path of files  
   string contropath = filterContext.Controller.ToString();

   //  Relative path to access directory 
   string filePath = filterContext.HttpContext.Request.FilePath;

   // url Full address 
   string url = (filterContext.HttpContext.Request.Url.AbsoluteUri).ExUrlDeCode();

   //  Request mode  post get
   string httpMethod = filterContext.HttpContext.Request.HttpMethod;

   //  Request IP Address 
   string ip = filterContext.HttpContext.Request.GetIPAddress();

   //  Get all request parameters 
   HttpRequest httpRequest = HttpContext.Current.Request;
   Dictionary<string, string> queryParameters = httpRequest.GetAllQueryParameters();

   //  Get exception object 
   Exception ex = filterContext.Exception;

   //  Exception description information 
   string exMessage = ex.Message;

   //  Exception stack information 
   string stackTrace = ex.StackTrace;

   //  Record logs according to the actual situation (text log, database log, it is recommended that the specific steps be completed asynchronously) 


   filterContext.ExceptionHandled = true;

   //  Simulation does corresponding logic processing according to different ones 
   int statusCode = filterContext.HttpContext.Response.StatusCode;

   if (statusCode>=400 && statusCode<500)
   {
    filterContext.Result = new RedirectResult("/html/404.html");
   }
   else 
   {
    filterContext.Result = new RedirectResult("/html/500.html");
   }
  }
 }

Exception handling instance code for API:


 /// <summary>
 /// API Custom exception handling mechanism 
 ///  When it comes to exception handling, in fact, the first in our mind 1 The reaction, too, should be try/cache Operation 
 ///  However, in actual development, it is very likely that address errors will not enter at all try In, or not by try Handled to exception 
 ///  This class plays a role, which can well handle uncaught exceptions and do corresponding logical processing 
 ///  Custom exception mechanism, mainly integrating ExceptionFilterAttribute  Rewrite its OnException Method 
 /// </summary>
 public class XYHAPIHandleError : ExceptionFilterAttribute
 {
  /// <summary>
  ///  Handle exceptions 
  /// </summary>
  /// <param name="actionExecutedContext"> Exception context </param>
  public override void OnException(HttpActionExecutedContext actionExecutedContext)
  {
   //  In our usual projects, exception handling 1 There are two functions in general 
   // 1: Detailed log of exceptions, which is convenient for post-analysis log 
   // 2: Unification of anomalies 1 Friendly handling, such as redirecting to a friendly prompt page according to the exception type 

   //  Both unprocessed exception information and request information can be obtained in this system 
   //  Here, you can do the corresponding logical processing according to the actual project needs 
   //  Here is a brief list of several key ways to obtain information 

   // action Name  
   string actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName;

   //  Controller name  
   string controllerName =actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerName;

   // url Full address 
   string url = (actionExecutedContext.Request.RequestUri.AbsoluteUri).ExUrlDeCode();

   //  Request mode  post get
   string httpMethod = actionExecutedContext.Request.Method.Method;

   //  Request IP Address 
   string ip = actionExecutedContext.Request.GetIPAddress();

   //  Get all request parameters 
   HttpRequest httpRequest = HttpContext.Current.Request;
   Dictionary<string, string> queryParameters = httpRequest.GetAllQueryParameters();

   //  Get exception object 
   Exception ex = actionExecutedContext.Exception;

   //  Exception description information 
   string exMessage = ex.Message;

   //  Exception stack information 
   string stackTrace = ex.StackTrace;

   //  Record logs according to the actual situation (text log, database log, it is recommended that the specific steps be completed asynchronously) 
   //  Logic of landing your own record log  ......

   //  Constructing system 1 The internal exception handling mechanism of the exception is equivalent to doing 1 Stratigraphic series 1 Packaging exposure 
   MBaseResult<string> result = new MBaseResult<string>()
   {
    Code = MResultCodeEnum.systemErrorCode,
    Message = MResultCodeEnum.systemError
   };

   actionExecutedContext.Response = new HttpResponseMessage(HttpStatusCode.OK);
   // You need to specify your own output content and type 
   HttpContext.Current.Response.ContentType = "text/html;charset=utf-8";
   HttpContext.Current.Response.Write(JsonConvert.SerializeObject(result));
   HttpContext.Current.Response.End(); //  End the response here, and it will not walk by the system 
  }
 }

Summarize

. net filter, my personal understanding of one sentence is: to action each stage of the unified monitoring and processing operations. . net filter, where the order of execution of each filter is: Authorize (Authorization)-- > ActionFilter (custom)-- > HandleError (Error Handling)

Ok, let's talk about it first. If something is wrong, give more advice and forgive me. I wrote an exercise DEMO myself, which will have instructions for dealing with each situation. If you are interested, you can download it and have a look. Thank you.

The address of DEMO in GitHub is: https://github.com/xuyuanhong0902/XYH.FilterTest.git


Related articles: