C# WebApi 過濾器的使用,開發接口必備利器


在WEB Api中,引入了面向切面編程(AOP)的思想,在某些特定的位置可以插入特定的Filter進行過程攔截處理。引入了這一機制可以更好地踐行DRY(Don’t Repeat Yourself)思想,通過Filter能統一地對一些通用邏輯進行處理,如:權限校驗、參數加解密、參數校驗等方面我們都可以利用這一特性進行統一處理,今天我們來介紹Filter的開發、使用以及討論他們的執行順序。

一、Filter的使用

         在默認的WebApi中,框架提供了三種Filter,他們的功能和運行條件如下表所示:

Filter 類型

實現的接口

描述

Authorization

IAuthorizationFilter

最先運行的Filter,被用作請求權限校驗

Action

IActionFilter

在Action運行的前、后運行

Exception

IExceptionFilter

當異常發生的時候運行

1.授權攔截過濾器

首先,我們實現一個CustomAuthorizeAttribute可以用以簡單的權限控制,(此處AuthorizeAttribute繼承自AuthorizationFilterAttribute)用於接口授權:

/// <summary>
    /// 自定義授權
    /// </summary>
     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class CustomAuthorizeAttribute:AuthorizeAttribute
    {
       /// <summary>
       /// 用戶授權
       /// </summary>
       /// <param name="actionContext"></param>
       /// <returns></returns>
        public override void OnAuthorization(HttpActionContext actionContext)
        {
            //url獲取token  
            var content = actionContext.Request.Properties["MS_HttpContext"] as HttpContextBase;
            HttpRequestBase request = content.Request;
            string access_key = request.Params["access_key"];//不管是post請求還是get請求,都從地址欄獲取key跟sign
            string sign = request.Params["sign"];
            //校驗IP
            var data=XmlUtil.IsAuthorization(HttpContext.Current.Request.UserHostAddress);
            if (data.IsExistence)//ip是否在白名單內
            {
                if ("1".Equals(data.IsAuthorization))//是否需要參與簽名驗證
                {
                    if (!string.IsNullOrEmpty(access_key) && !string.IsNullOrEmpty(sign))
                    {
                        var key = "7058e63cdf0646948201";//隨便固定一個key,真實使用的化可以從數據庫獲取或配置文件讀取
                        var s = MD5Encrypt32(string.Format("{0}{1}", access_key, key));
                        if ("2c3a368b-26a8-4a4d-a204-14bd6388f3c2".Equals(access_key) && s.Equals(sign))//驗證通過放行
                        {
                            base.IsAuthorized(actionContext);
                        }
                        else
                        {
                            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, new
                            {
                                code = HttpStatusCode.Unauthorized,
                                data = "",
                                message = "access_key 或 sign 參數有誤,請核對",
                            });
                        }
                    }
                    else
                    {
                        actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, new
                        {
                            code = HttpStatusCode.Unauthorized,
                            data = "",
                            message = "access_key 或 sign 校驗參數未從地址欄帶入,請核對",
                        });
                    }
                }
                else
                {
                    base.IsAuthorized(actionContext);
                }
            }
            else
            {
                actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, new
                {
                    code = HttpStatusCode.Unauthorized,
                    data = "",
                    message = $"IP:{HttpContext.Current.Request.UserHostAddress}沒有調用服務的權限",
                });
            }
        }
       /// <summary>
       /// 32位MD5加密
       /// </summary>
       /// <param name="password"></param>
       /// <returns></returns>
       public  string MD5Encrypt32(string password)
       {
           string pwd = "";
           MD5 md5 = MD5.Create(); //實例化一個md5對像
           // 加密后是一個字節類型的數組,這里要注意編碼UTF8/Unicode等的選擇 
           byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(password));
           // 通過使用循環,將字節類型的數組轉換為字符串,此字符串是常規字符格式化所得
           for (int i = 0; i < s.Length; i++)
           {
               // 將得到的字符串使用十六進制類型格式。格式后的字符是小寫的字母,如果使用大寫(X)則格式后的字符是大寫字符 
               pwd = pwd + s[i].ToString("X");
           }
           return pwd.ToLower();
       }
    }

 2.錯誤異常捕獲過濾器

當服務端代碼報錯或出異常時,可自定義設置固定格式的異常返回給調用者,具體實現如下:

/// <summary>
    /// 自定義錯誤異常捕獲
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class CustomExceptionAttribute:ExceptionFilterAttribute
    {
        /// <summary>
        /// 異常發生
        /// </summary>
        /// <param name="actionExecutedContext"></param>
        public override void OnException(HttpActionExecutedContext actionExecutedContext)
        {
            //記錄錯誤日志
            Task.Run(() =>
            {
                //此處可以調用記錄日志方法
            });
            actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(HttpStatusCode.InternalServerError, new
            {
                code = HttpStatusCode.InternalServerError,
                message = actionExecutedContext.Exception.Message
            });
        }
    }

 3.請求成功過濾器

如果要把所有接口返回類型參數固定可以使用ActionFilter過濾器,簡單實現如下:

/// <summary>
    /// 自定義統一信息格式返回
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class CustomActionAttribute:ActionFilterAttribute
    {
        /// <summary>
        /// 請求成功之后
        /// </summary>
        /// <param name="actionExecutedContext"></param>
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(actionExecutedContext.Response.StatusCode, new
            {
                code = 200,
                data =JsonConvert.DeserializeObject(actionExecutedContext.Response.Content.ReadAsStringAsync().Result),//返回給調用者的數據
                message ="success"
            });
        }
    }

 

接口調用成功之后返回給調用者的數據格式都是固定格式,調用者使用起來就很方便,返回固定格式如下:

{
   code = 200,
   data ="數據"
   message ="success"
}

 

 二、過濾器的使用及注冊

過濾器注冊方式有三種:

  • 全局注冊
  • 類注冊
  • 方法注冊

1.全局注冊

全局注冊時在App_Start==>WebApiConfig.cs文件里面,注冊代碼如下:

public static class WebApiConfig
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="config"></param>
        public static void Register(HttpConfiguration config)
        {
            // 身份認證篩選器。
            config.Filters.Add(new CustomAuthorizeAttribute());//這是全局注冊

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            config.EnsureInitialized();
        }
    }

2.控制器注冊

控制器注冊就是在Controller類里面進行注冊,注冊代碼如下:

/// <summary>
/// 
/// </summary>
[CustomAuthorizeAttribute] //控制器注冊
public class DepartmentApiController : ApiController {



 } 

3.方法注冊

方法注冊就是在Controller里面使用的方法上進行標記注冊,注冊代碼如下:

/// <summary>
/// 
/// </summary>
public class DepartmentApiController : ApiController {

 [CustomAuthorizeAttribute]//方法注冊
 [HttpGet]
  public object GetDepartmentList()
  {

  }
}

三、過濾器執行優先級

  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,AllowMultiple = false)]//AllowMultiple=false時 執行優先級如下 

同一個過濾器分別用在了全局、控制器和行為方法中,執行同一個方法時都會有先后順序,如果按默認值(不設Order的情況下),一般的順序是由最外層到最里層,就是“全局”——>“控制器”——>“行為方法”;

而特別的就是錯誤處理的過濾器,由於異常是由里往外拋的,所以它的順序剛好也反過來:“行為方法”——>“控制器”——>“全局”。

以上是開發webapi過濾器的簡單用法,以上代碼還需要優化,有需要的朋友自己封裝優化一下就可以使用了。


免責聲明!

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



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