思路:
通过重写 ActionFilterAttribute 拦截Action的请求及返回信息,实现对接口请求的监听。
最终效果如下:
全局启用需配置如下:
局部启用需配置如下:
[TimingFilter("相关接口")]
[RoutePrefix("PassPort")]
public class PassPortController : ApiController
{
}
源码如下:
/// <summary>
/// 统一的接口访问监控日志,
/// 全局启用-请添加 GlobalConfiguration.Configuration.Filters.Add(new TimingFilterAttribute());
/// 局部启用(目前模式)-在Controller或者Action上添加Attribute注入即可
/// </summary>
public class TimingFilterAttribute : ActionFilterAttribute
{
private const string Key = "__action_recordtime__";
private const string TimeKey = "__action_receivetime__";
/// <summary>
/// 自定义参数
/// </summary>
public static string msg { get; set; }
public static string ReqParms { get; set; }
public TimingFilterAttribute()
{
msg = string.Empty;
}
/// <summary>
/// 初始化时填入类的说明
/// </summary>
/// <param name="message"></param>
public TimingFilterAttribute(string message)
{
msg = message;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (IsRecordTime(actionContext))
{
return;
}
// ReqParms= GetRequestValues(actionContext);
var stopWatch = new Stopwatch();
actionContext.Request.Properties[Key] = stopWatch;
actionContext.Request.Properties[TimeKey] = DateTime.Now.ToBinary();
stopWatch.Start();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (!actionExecutedContext.Request.Properties.ContainsKey(Key) || !actionExecutedContext.Request.Properties.ContainsKey(TimeKey))
{
return;
}
object beginTime = null;
if (!actionExecutedContext.Request.Properties.TryGetValue(TimeKey, out beginTime))
{
return;
}
var stopWatch = actionExecutedContext.Request.Properties[Key] as Stopwatch;
if (stopWatch == null)
{
return;
}
stopWatch.Stop();
DateTime starttime = DateTime.FromBinary(Convert.ToInt64(beginTime));
HttpRequest request = HttpContext.Current.Request;
string token = string.Empty;
var appkey = string.Empty;
if (request.Headers.AllKeys.Contains("Token")) { token = request.Headers["Token"]; }
if (request.Headers.AllKeys.Contains("AppKey")) { appkey = request.Headers["AppKey"]; }
var actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName;
var controllerName = actionExecutedContext.ActionContext.ActionDescriptor.ControllerDescriptor.ControllerName;
var ip = request.UserHostAddress;
var UserHostName = request.UserHostName;
var paramaters = GetRequestValues(actionExecutedContext);
// var paramaters = ReqParms;//获取入参
var executeResult = GetResponseValues(actionExecutedContext);//获取response响应的结果
var comments = msg;
var RequestUri = request.Url.AbsoluteUri;
var MethodType= request.HttpMethod.ToString();
Bondex.Core.BizTool.LogPlat.LogHelper.Info($"{controllerName}/{actionName}", "E帐通接口监控服务", "TimingFilterAttribute", $"",
$"当前接口路径:{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},备注:{comments}");
}
/// <summary>
/// 读取request 的提交内容
/// </summary>
/// <param name="actionExecutedContext"></param>
/// <returns></returns>
public string GetRequestValues(HttpActionExecutedContext actionExecutedContext)
{
string result = String.Empty;
using (var stream = actionExecutedContext.Request.Content.ReadAsStreamAsync().Result)
{
if (stream.CanSeek)
{
stream.Position = 0;
}
result = actionExecutedContext.Request.Content.ReadAsStringAsync().Result;
}
return result;
}
/// <summary>
/// 读取action返回的result
/// </summary>
/// <param name="actionExecutedContext"></param>
/// <returns></returns>
public string GetResponseValues(HttpActionExecutedContext actionExecutedContext)
{
Stream stream = actionExecutedContext.Response.Content.ReadAsStreamAsync().Result;
Encoding encoding = Encoding.UTF8;
/*
这个StreamReader不能关闭,也不能dispose, 关了就傻逼了
因为你关掉后,后面的管道 或拦截器就没办法读取了
*/
var reader = new StreamReader(stream, encoding);
string result = reader.ReadToEnd();
/*
这里也要注意: stream.Position = 0;
当你读取完之后必须把stream的位置设为开始
因为request和response读取完以后Position到最后一个位置,交给下一个方法处理的时候就会读不到内容了。
*/
stream.Position = 0;
return result;
}
/// <summary>
/// 判断是否拦截请求,记录接口信息
/// </summary>
/// <param name="actionContext"></param>
/// <returns></returns>
private static bool IsRecordTime(HttpActionContext actionContext)
{
return actionContext.ActionDescriptor.GetCustomAttributes<NoRecordTimeAttribute>().Any() ||
actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<NoRecordTimeAttribute>().Any();
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]
public class NoRecordTimeAttribute : Attribute
{
}