在WEB Api中,引入了面向切面編程(AOP)的思想,在某些特定的位置可以插入特定的Filter進行過程攔截處理。引入了這一機制可以更好地踐行DRY(Don’t Repeat Yourself)思想,通過Filter能統一地對一些通用邏輯進行處理,如:權限校驗、參數加解密、參數校驗等方面我們都可以利用這一特性進行統一處理,今天我們來介紹Filter的開發、使用以及討論他們的執行順序。
一、Filter的開發和調用
在默認的WebApi中,框架提供了三種Filter,他們的功能和運行條件如下表所示:
Filter 類型 |
實現的接口 |
描述 |
Authorization |
IAuthorizationFilter |
最先運行的Filter,被用作請求權限校驗 |
Action |
IActionFilter |
在Action運行的前、后運行 |
Exception |
IExceptionFilter |
當異常發生的時候運行 |
首先,我們實現一個AuthFilterOutside可以用以簡單的權限控制:
public class AuthFilterOutside: AuthorizeAttribute { private SP_PortUserBLL sp_portuserbll = new SP_PortUserBLL(); //重寫基類的驗證方式,加入我們自定義的Ticket驗證 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.Form["access_key"];//獲取請求參數對應的值 string sign = request.Form["sign"]; if (!string.IsNullOrEmpty(access_key) && !string.IsNullOrEmpty(sign)) { //解密用戶ticket,並校驗用戶名密碼是否匹配 if (ValidateTicket(access_key, sign)) { base.IsAuthorized(actionContext); } else { HandleUnauthorizedRequest(actionContext); } } //如果取不到身份驗證信息,並且不允許匿名訪問,則返回未驗證401 else { var attributes = actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().OfType<AllowAnonymousAttribute>(); bool isAnonymous = attributes.Any(a => a is AllowAnonymousAttribute); if (isAnonymous) base.OnAuthorization(actionContext); else HandleUnauthorizedRequest(actionContext); } } //校驗sign(數據庫數據匹配) private bool ValidateTicket(string key,string sign) { var result=sp_portuserbll.GetAccess_secret(key); if (!string.IsNullOrEmpty(result)) { var mysing= Encryption.DataEncryption(key, result);//sign驗證 if (mysing.Equals(sign)) { return true; } return false; } return false; } }
當請求地址里面包含 access_key 和 sign 對應的鍵值對,獲取對應的值與數據庫數據進行匹配,匹配通過后可請求數據,適用於get 、post請求。
接口請求成功后記錄日志的實現
/// <summary> /// 請求成功后觸發 /// </summary> public class AuthFilter: ActionFilterAttribute { private PortLogBLL portlogbll = new PortLogBLL(); public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { //action 請求之后觸發<br> //日志記錄 訪問量記錄等等 portlogbll.SaveForm(new PortLogEntity() { PortName = actionExecutedContext.Request.RequestUri.AbsolutePath,//獲得調用接口, RequestType = actionExecutedContext.Request.Method.ToString(), StatusCode = Convert.ToInt32(new HttpResponseMessage(HttpStatusCode.OK).StatusCode),//設置狀態碼 ClientIp = GetClientIp(), ParameterList = actionExecutedContext.ActionContext.ActionArguments.ToJson(),//獲得參數值 Success = true }); } /// <summary> /// 獲取客戶端Ip /// </summary> /// <returns></returns> private string GetClientIp() { string result = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]; if (string.IsNullOrEmpty(result)) { result = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"]; } if (string.IsNullOrEmpty(result)) { result = HttpContext.Current.Request.UserHostAddress; } if (string.IsNullOrEmpty(result)) { return "0.0.0.0"; } return result; } }
當服務端代碼報錯或出異常時,可自定義設置固定格式的異常返回給調用者
/// <summary> /// 接口發生異常過濾器 /// </summary> public class ExceptionHandling : ExceptionFilterAttribute, IExceptionFilter { private PortLogBLL portlogbll = new PortLogBLL(); public override void OnException(HttpActionExecutedContext actionExecutedContext) { var code = new HttpResponseMessage(HttpStatusCode.InternalServerError).StatusCode;//設置錯誤代碼:例如:500 404 actionExecutedContext.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError); string msg = JsonConvert.SerializeObject(new BaseResult() { success = false, message = actionExecutedContext.Exception.Message });//返回異常錯誤提示 //寫入錯誤日志相關實現 portlogbll.SaveForm(new PortLogEntity() { PortName = actionExecutedContext.Request.RequestUri.AbsolutePath, RequestType = actionExecutedContext.Request.Method.ToString(), StatusCode = Convert.ToInt32(code), ClientIp = GetClientIp(), ParameterList = actionExecutedContext.ActionContext.ActionArguments.ToJson(), Success = false, ErrorMessage = msg }); //result actionExecutedContext.Response.Content = new StringContent(msg, Encoding.UTF8); } /// <summary> /// 獲取調用接口者ip地址 /// </summary> /// <returns></returns> private string GetClientIp() { string result = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]; if (string.IsNullOrEmpty(result)) { result = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"]; } if (string.IsNullOrEmpty(result)) { result = HttpContext.Current.Request.UserHostAddress; } if (string.IsNullOrEmpty(result)) { return "0.0.0.0"; } return result; } } public class BaseResult { /// <summary> /// 狀態 /// </summary> public bool success { get; set; } /// <summary> /// 錯誤信息 /// </summary> public string message { get; set; } }
以上是開發webapi常用代碼,自己封裝一下就可以使用了