Have a chat Asp.net Filter filter

Time:2020-7-29

Recently, when sorting and optimizing. Net code, we found several unfriendly processing phenomena: login judgment, authority authentication, logging, exception handling and other general operations, which are everywhere in the action of the project. In code optimization, this is a very important focus. At this time, the filters and interceptors in. Net come into play. Now, according to the actual work of these days, I have made a simple comb and shared it for your reference and exchange. If there is any improper writing, please point out and exchange more.

summary:

Net filter mainly includes the following four categories: authorize, actionfilter and handleerror.

filter

Class name

Implementation interface

describe

to grant authorization

AuthorizeAttribute

IAuthorizationFilter

This type (or filter) is used to restrict access to the controller or a behavior method of the controller, such as login, permission, access control, and so on

abnormal

HandleErrorAttribute

IExceptionFilter

It is used to specify a behavior, which handles the exception thrown by a behavior method or a controller, for example, global exception handling.

custom

ActionFilterAttribute

Iationfilter and iresultfilter

Used for processing before or after entering a behavior or before or after returning a result, such as user request log details logging

Authorizeattribute: authentication and authorization

Authentication and authorization is mainly the first access authentication for all actions, and the first supervision, filtering and intercepting gate for users’ access.

Implementation method: we need to define a class, inherit the authorizeattribute and override onauthorization. In onauthorization, we can get all the request information of the user’s request. In fact, all the data support of all the authentication and interception operations we do comes from the request.

Specific verification process design:

IP whitelist: This is mainly for the API to do IP restrictions, only the specified IP can be accessed, non specified IP directly returned

Request frequency control: This is mainly used to control the user’s access frequency, mainly for the API. If it exceeds the request frequency, it will be returned directly.

Login authentication: generally, we use the method of passing token in the header of the request to verify the login authentication. In this way, both MVC login authentication and auth authentication with API interface are 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 the injection mode, it mainly includes: global, controller controller and action

Global registration: all acitons for all systems use

Controller: only works for actions under the controller

Action: only works for this action

There are some differences between MVC and API for global registration

MVC in FilterConfig.cs Medium injection


filters.Add(new XYHMVCAuthorizeAttribute());

API in WebApiConfig.cs Medium injection


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

Note: in actual use, we usually add global authentication for authentication and authorization. However, some actions do not need to be authenticated, such as the original login action. How to exclude it? In fact, it is also very simple. We only need to define a custom attribute to integrate the attribute or allowanymousattribute of the system. In the action that does not need to be verified, we just need to register the attribute for and do a filter before validation, for example:

//The interface with allowanonymous property is directly on green
   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)
  {
   //The interface with allowanonymous property is directly on green
   if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any())
   {
    return;
   }

   //A layer of interception is done before the request, mainly to verify the validity of the token and verify the signature
   HttpRequest httpRequest = HttpContext.Current.Request;

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

   //First do IP whitelist verification 
   MBaseResult<string> result = new AuthCheckService().CheckIpWhitelist(FilterAttributeHelp.GetIPAddress(actionContext.Request), apikey);

   //Inspection time
   string timestamp = httpRequest.QueryString["Timestamp"];
   if (result.Code == MResultCodeEnum.successCode)
   {
    //Inspection time 
    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 a nochekokenfilterattribute tag, no token authentication is required
     if (actionContext.ActionDescriptor.GetCustomAttributes<XYHAPINoChekokenFilterAttribute>().Any())
     {
      return;
     }

     //Check the validity of token
     //Get 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)
   {
    //Be sure to instantiate a response. Will the code in the action be executed eventually
    actionContext.Response = new HttpResponseMessage(HttpStatusCode.OK);
    //You need to specify the output content and type yourself
    HttpContext.Current.Response.ContentType = "text/html;charset=utf-8";
    HttpContext.Current.Response.Write(JsonConvert.SerializeObject(result));
    HttpContext.Current.Response . end(); // if you end the response here, you will not walk by the system
   }
  }
 }

MVC authfilterattribute instance code

/// <summary>
 ///MVC custom authorization
 ///There are two override methods for authentication authorization
 ///Specific authentication logic implementation: the specific authentication logic is written in the authorizecore. If the authentication is successful, it returns true, otherwise it returns false
 ///Authentication failure processing logic: handleunauthorized request will be executed in this method when the previous step returns false
 ///However, in the application process, I usually do the logical processing after authentication directly in the authorized core according to different authentication results
 /// </summary>
 public class XYHMVCAuthorizeAttribute : AuthorizeAttribute
 {
  /// <summary>
  ///Authentication logic
  /// </summary>
  ///< param name = "filtercontext" > filter context < / param >
  public override void OnAuthorization(AuthorizationContext filterContext)
  {

   //Here we mainly write the relevant verification logic of authentication and authorization
   //The verification of this part generally includes two parts
   //Login authority verification
   //-- our general processing method is to pass a token in the header for logical verification
   //-- of course, different systems are not the same in design, and some systems will also use session and other methods to verify
   //-- therefore, the corresponding logical operation is finally carried out according to the actual situation of the project itself

   //Specific page authority verification
   //-- the validation in this section is specific to page permission verification
   //-- I think some of the partners failed to achieve this level. They directly put this step in the front-end JS to verify. This is not very safe, but it can block small white users
   //-- of course, some systems do not have permission control at all, so this logic is not needed.
   //-- therefore, the corresponding logical operation is finally carried out according to the actual situation of the project itself

   //Now with a crude way to simulate the implementation of a simple, with the current system time period seconds cooking 3, take the remainder
   //When the remainder is 0: the authentication and authorization are passed
   //1: for login, adjust to the login page
   //2: on behalf of no access rights, adjust to the no permission prompt page

   //Of course, you can also do some IP whitelist, IP blacklist verification, request frequency verification and so on

   //Speaking of this, we should also note that if we choose to register the filter globally, then if some pages do not need permission authentication at all, such as the login page, we can add a special Annotation "allowanonymous" to the controller or action that does not need permission authentication

   //Get some key information of request
   HttpRequest httpRequest = HttpContext.Current.Request;
   string acitonName = filterContext.ActionDescriptor.ActionName;
   string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;

   //Note: if the authentication fails, it needs to be set filterContext.Result Otherwise, the logic in the action will still be executed

   filterContext.Result = null;
   int thisSecond = System.DateTime.Now.Second;
   switch (thisSecond % 3)
   {
    case 0:
     //Certification and authorization passed
     break;
    case 1:
     //For login, adjust to the login page
     //The operation will not be terminated until result is set
     filterContext.Result = new RedirectResult("/html/Login.html");
     break;
    case 2:
     //On behalf of no access rights, adjust to the no permission prompt page
     filterContext.Result = new RedirectResult("/html/NoAuth.html");
     break;
   }
  }
 }

Actionfilter: custom filter

The custom filter is mainly used to monitor the events before and after the action request and the event before and after the result return. The API has only two methods before and after the request.

New method

Method function description

Used in

OnActionExecuting

A request is executed before entering the aciton logic

MVC、API

OnActionExecuted

A request is executed after the aciton logic is executed

MVC、API

OnResultExecuting

The corresponding view view is executed before rendering

MVC

OnResultExecuted

The corresponding view view is executed after rendering

MVC

In these methods, we usually record the interaction log and record the time consumption of each step, so as to optimize the use of the subsequent system. Specific use, according to their own business scenarios.

The similarities and differences between MVC and API are similar to the above mentioned authentication and authorization, which will not be explained in detail.

Here is an example code:

API definition filter instance demo code

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

  }

  /// <summary>
  ///After the action is executed
  /// </summary>
  /// <param name="actionContext"></param>
  public override void OnActionExecuted(HttpActionExecutedContext actionContext)
  {
   try
   {
    //Building a 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();

    //Since the database can only store 4000 strings at most, an intercept is made for the return value
    if (!string.IsNullOrEmpty(apiRequestLogsM.RESPONSE_INFOR) &&
     apiRequestLogsM.RESPONSE_INFOR.Length > 4000)
    {
     apiRequestLogsM.RESPONSE_INFOR = apiRequestLogsM.RESPONSE_INFOR.Substring(0, 2000);
    }

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

    //Define an asynchronous delegate to log asynchronously
    //Func < mapirequestlogs, string > action = addapirequestlogs; // declare a delegate
    // IAsyncResult ret = action.BeginInvoke(apiRequestLogsM, null, null);

   }
   catch (Exception ex)
   {

   }
  }
 }

Handleerror: error handling

Exception handling is very common for us. It can avoid the try / catch of the whole article by making good use of exception handling. The exception handling bin list is very simple. The value needs to be customized and integrated: exceptionfilterattribute, and the onexception method can be customized.

In onexception, we can do some corresponding logical processing according to our own needs, such as recording the exception log, so as to facilitate the analysis and follow-up of subsequent problems.

Onexception also has a very important processing, that is, the unified packaging of exception results, return a very friendly result to the user, avoid returning some unnecessary information to the user. For example: for MVC, follow up different exceptions and adjust to friendly prompt page; for API, we can return several packages in a unified way, so that users can handle the results uniformly.

MVC exception handling example code:

/// <summary>
 ///MVC custom exception handling mechanism
 ///When it comes to exception handling, the first reaction in our mind should also be try / cache operation
 ///However, in the actual development, it is very likely that the address error can not enter the try at all, or it may not be handled by the try
 ///This class plays a role, can be a good catch of the exception, and do the corresponding logic processing
 ///It mainly integrates handleerrorattribute and rewrites its onexception method
 /// </summary>
 public class XYHMVCHandleError : HandleErrorAttribute
 {
  /// <summary>
  ///Handling exceptions
  /// </summary>
  ///< param name = "filtercontext" > exception context < / param >
  public override void OnException(ExceptionContext filterContext)
  {
   //In our usual project, exception handling has two functions
   //1: record the detailed log of the exception, which is convenient for post analysis log
   //2: unified and friendly handling of exceptions, such as redirecting to the friendly prompt page according to the exception type

   //In this, we can not only get the unhandled exception information, but also get the request information
   //Here we can do the corresponding logical processing according to the actual project needs
   //The following is a simple list of several key information acquisition methods

   //Note the name of the controller. The full path of a file is obtained in this way 
   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();

   //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;

   //Log according to the actual situation (text log, database log, it is recommended to use asynchronous method to complete the specific steps)


   filterContext.ExceptionHandled = true;

   //Simulation according to different logic processing
   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");
   }
  }
 }

API exception handling example code:

/// <summary>
 ///API custom exception handling mechanism
 ///When it comes to exception handling, the first reaction in our mind should also be try / cache operation
 ///However, in the actual development, it is very likely that the address error can not enter the try at all, or it may not be handled by the try
 ///This class plays a role, can be a good catch of the exception, and do the corresponding logic processing
 ///It mainly integrates the exceptionfilterattribute and rewrites its onexception method
 /// </summary>
 public class XYHAPIHandleError : ExceptionFilterAttribute
 {
  /// <summary>
  ///Handling exceptions
  /// </summary>
  ///< param name = "actionexecuted context" > exception context < / param >
  public override void OnException(HttpActionExecutedContext actionExecutedContext)
  {
   //In our usual project, exception handling has two functions
   //1: record the detailed log of the exception, which is convenient for post analysis log
   //2: unified and friendly handling of exceptions, such as redirecting to the friendly prompt page according to the exception type

   //In this, we can not only get the unhandled exception information, but also get the request information
   //Here we can do the corresponding logical processing according to the actual project needs
   //The following is a simple list of several key information acquisition methods

   //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();

   //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;

   //Log according to the actual situation (text log, database log, it is recommended to use asynchronous method to complete the specific steps)
   //My own log landing logic is simple

   //The construction of a unified internal exception handling mechanism is equivalent to a unified packaging and exposure of exceptions
   MBaseResult<string> result = new MBaseResult<string>()
   {
    Code = MResultCodeEnum.systemErrorCode,
    Message = MResultCodeEnum.systemError
   };

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

summary

. net filter, my personal understanding is: unified monitoring and processing of all stages of action. In. Net filter, the execution order of each filter is: authorize > actionfilter > handleerror

OK, let’s talk about it first. If something is wrong, give me more advice and forgive me. I wrote an exercise demo myself, in which there will be instructions for handling 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

Let’s talk about it here Asp.net This is what the article about filterfilter Asp.net Please search the previous articles of developeppaer or continue to browse the related articles below. I hope you can support developeppaer more in the future!