在WEB Api中,引入了面向切面編程(AOP)的思想,在某些特定的位置能夠插入特定的Filter進行過程攔截處理。引入了這一機制能夠更好地踐行DRY(Don’t Repeat Yourself)思想,經過Filter能統一地對一些通用邏輯進行處理,如:權限校驗、參數加解密、參數校驗等方面咱們均可以利用這一特性進行統一處理,今天咱們來介紹Filter的開發、使用以及討論他們的執行順序。web
在默認的WebApi中,框架提供了三種Filter,他們的功能和運行條件以下表所示:數據庫
Filter 類型編程 |
實現的接口api |
描述數組 |
Authorization框架 |
IAuthorizationFilteride |
最早運行的Filter,被用做請求權限校驗post |
Action優化 |
IActionFilter編碼 |
在Action運行的前、後運行 |
Exception |
IExceptionFilter |
當異常發生的時候運行 |
首先,咱們實現一個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(); } }
當服務端代碼報錯或出異常時,可自定義設置固定格式的異常返回給調用者,具體實現以下:
/// <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 }); } }
若是要把全部接口返回類型參數固定可使用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" }
過濾器註冊方式有三種:
全局註冊時在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(); } }
控制器註冊就是在Controller類裏面進行註冊,註冊代碼以下:
/// <summary> /// /// </summary> [CustomAuthorizeAttribute] //控制器註冊 public class DepartmentApiController : ApiController { }
方法註冊就是在Controller裏面使用的方法上進行標記註冊,註冊代碼以下:
/// <summary> /// /// </summary> public class DepartmentApiController : ApiController { [CustomAuthorizeAttribute]//方法註冊 [HttpGet] public object GetDepartmentList() { }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,AllowMultiple = false)]//AllowMultiple=false時 執行優先級以下
同一個過濾器分別用在了全局、控制器和行爲方法中,執行同一個方法時都會有前後順序,若是按默認值(不設Order的狀況下),通常的順序是由最外層到最裏層,就是「全局」——>「控制器」——>「行爲方法」;
而特別的就是錯誤處理的過濾器,因爲異常是由裏往外拋的,因此它的順序恰好也反過來:「行爲方法」——>「控制器」——>「全局」。
以上是開發webapi過濾器的簡單用法,以上代碼還須要優化,有須要的朋友本身封裝優化一下就可使用了。