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

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

1、Filter的使用

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

Filter 類型編程

實現的接口api

描述數組

Authorization框架

IAuthorizationFilteride

最早運行的Filter,被用做請求權限校驗post

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"
}

 

 2、過濾器的使用及註冊

過濾器註冊方式有三種:

  • 全局註冊
  • 類註冊
  • 方法註冊

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()
  {

  }
}

3、過濾器執行優先級

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

同一個過濾器分別用在了全局、控制器和行爲方法中,執行同一個方法時都會有前後順序,若是按默認值(不設Order的狀況下),通常的順序是由最外層到最裏層,就是「全局」——>「控制器」——>「行爲方法」;

而特別的就是錯誤處理的過濾器,因爲異常是由裏往外拋的,因此它的順序恰好也反過來:「行爲方法」——>「控制器」——>「全局」。

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

相關文章
相關標籤/搜索