在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常用代码,自己封装一下就可以使用了