思路:
通过重写 ActionFilterAttribute 拦截Action的请求及返回信息,实现对接口请求的监听。
最终效果如下:
全局启用需配置如下:
局部启用需配置如下:
源码如下:
1 /// <summary> 2 /// 统一的接口访问监控日志, 3 /// 全局启用-请添加 GlobalConfiguration.Configuration.Filters.Add(new TimingFilterAttribute()); 4 /// 局部启用(目前模式)-在Controller或者Action上添加Attribute注入即可 5 /// </summary> 6 public class TimingFilterAttribute : ActionFilterAttribute 7 { 8 private const string Key = "__action_recordtime__"; 9 private const string TimeKey = "__action_receivetime__"; 10 /// <summary> 11 /// 自定义参数 12 /// </summary> 13 public string Message { get; set; } 14 15 public override Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken) 16 { 17 if (IsRecordTime(actionContext)) 18 { 19 return base.OnActionExecutingAsync(actionContext, cancellationToken); 20 } 21 // ReqParms= GetRequestValues(actionContext); 22 var stopWatch = new Stopwatch(); 23 actionContext.Request.Properties[Key] = stopWatch; 24 actionContext.Request.Properties[TimeKey] = DateTime.Now.ToBinary(); 25 stopWatch.Start(); 26 return base.OnActionExecutingAsync(actionContext, cancellationToken); 27 } 28 29 public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) 30 { 31 if (!actionExecutedContext.Request.Properties.ContainsKey(Key) || !actionExecutedContext.Request.Properties.ContainsKey(TimeKey)) 32 { 33 return base.OnActionExecutedAsync(actionExecutedContext, cancellationToken); 34 } 35 object beginTime = null; 36 if (!actionExecutedContext.Request.Properties.TryGetValue(TimeKey, out beginTime)) 37 { 38 return base.OnActionExecutedAsync(actionExecutedContext, cancellationToken); 39 } 40 var stopWatch = actionExecutedContext.Request.Properties[Key] as Stopwatch; 41 if (stopWatch == null) 42 { 43 return base.OnActionExecutedAsync(actionExecutedContext, cancellationToken); 44 } 45 stopWatch.Stop(); 46 47 DateTime starttime = DateTime.FromBinary(Convert.ToInt64(beginTime)); 48 HttpRequest request = HttpContext.Current.Request; 49 string token = string.Empty; 50 var appkey = string.Empty; 51 if (request.Headers.AllKeys.Contains("Token")) { token = request.Headers["Token"]; } 52 53 if (request.Headers.AllKeys.Contains("AppKey")) { appkey = request.Headers["AppKey"]; } 54 55 var actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName; 56 var controllerName = actionExecutedContext.ActionContext.ActionDescriptor.ControllerDescriptor.ControllerName; 57 58 var ip = request.UserHostAddress; 59 var UserHostName = request.UserHostName; 60 var paramaters = GetRequestValues(actionExecutedContext); 61 // var paramaters = ReqParms;//获取入参 62 var executeResult = GetResponseValues(actionExecutedContext);//获取response响应的结果 63 64 var RequestUri = request.Url.AbsoluteUri; 65 var MethodType= request.HttpMethod.ToString(); 66 Bondex.Core.BizTool.LogPlat.LogHelper.Info($"{controllerName}/{actionName}", "E帐通接口监控服务", "TimingFilterAttribute", $"", 67 $"当前接口路径:{RequestUri},请求方式:{MethodType},请求时间:{starttime.ToString("yyyy-MM-dd HH:mm:ss:fff")},返回时间{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")},接口执行时间:{stopWatch.Elapsed};请求IP:{ip},请求DNS:{UserHostName},请求入参:{paramaters},返回值:{executeResult},备注:{Message}"); 68 return base.OnActionExecutedAsync(actionExecutedContext, cancellationToken); 69 70 } 71 /// <summary> 72 /// 读取request 的提交内容 73 /// </summary> 74 /// <param name="actionExecutedContext"></param> 75 /// <returns></returns> 76 public string GetRequestValues(HttpActionExecutedContext actionExecutedContext) 77 { 78 string result = String.Empty; 79 using (var stream = actionExecutedContext.Request.Content.ReadAsStreamAsync().Result) 80 { 81 if (stream.CanSeek) 82 { 83 stream.Position = 0; 84 } 85 result = actionExecutedContext.Request.Content.ReadAsStringAsync().Result; 86 } 87 88 return result; 89 } 90 91 /// <summary> 92 /// 读取action返回的result 93 /// </summary> 94 /// <param name="actionExecutedContext"></param> 95 /// <returns></returns> 96 public string GetResponseValues(HttpActionExecutedContext actionExecutedContext) 97 { 98 Stream stream = actionExecutedContext.Response.Content.ReadAsStreamAsync().Result; 99 Encoding encoding = Encoding.UTF8; 100 /* 101 这个StreamReader不能关闭,也不能dispose, 关了就傻逼了 102 因为你关掉后,后面的管道 或拦截器就没办法读取了 103 */ 104 var reader = new StreamReader(stream, encoding); 105 string result = reader.ReadToEnd(); 106 /* 107 这里也要注意: stream.Position = 0; 108 当你读取完之后必须把stream的位置设为开始 109 因为request和response读取完以后Position到最后一个位置,交给下一个方法处理的时候就会读不到内容了。 110 */ 111 stream.Position = 0; 112 return result; 113 } 114 /// <summary> 115 /// 判断是否拦截请求,记录接口信息 116 /// </summary> 117 /// <param name="actionContext"></param> 118 /// <returns></returns> 119 private static bool IsRecordTime(HttpActionContext actionContext) 120 { 121 return actionContext.ActionDescriptor.GetCustomAttributes<NoRecordTimeAttribute>().Any() || 122 actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<NoRecordTimeAttribute>().Any(); 123 } 124 } 125 126 [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)] 127 public class NoRecordTimeAttribute : Attribute 128 { 129 130 }
努力不给自己一点懒惰的理由,否则你会一蹶不振