巧用ActionFilterAttribute实现API日志的记录

 本文转载自微信公众号「UP技术控」,作者conan5566。转载本文请联系UP技术控公众号。  

背景

上回提到开发web api的时候,一般是需要记录api的输入输出信息,方便后续排查问题;使用的是委托的形式进行记录日志。

使用Func

这次我们使用另外一种方式,Filter来记录输入输出日志。

实现方式

1、首先在进入action的时候,定义OnActionExecuting。

 
 
 
  1. public override void OnActionExecuting(ActionExecutingContext context) 
  2.         { 
  3.             base.OnActionExecuting(context); 
  4.  
  5.             // 后续添加了获取请求的请求体,如果在实际项目中不需要删除即可 
  6.             long contentLen = context.HttpContext.Request.ContentLength == null ? 0 : context.HttpContext.Request.ContentLength.Value; 
  7.             if (contentLen > 0) 
  8.             { 
  9.                 // 读取请求体中所有内容 
  10.                 System.IO.Stream stream = context.HttpContext.Request.Body; 
  11.                 if (context.HttpContext.Request.Method == "POST") 
  12.                 { 
  13.                     stream.Position = 0; 
  14.                 } 
  15.                 byte[] buffer = new byte[contentLen]; 
  16.                 stream.Read(buffer, 0, buffer.Length); 
  17.                 // 转化为字符串 
  18.                 RequestBody = System.Text.Encoding.UTF8.GetString(buffer); 
  19.             } 
  20.  
  21.             ActionArguments = Newtonsoft.Json.JsonConvert.SerializeObject(context.ActionArguments); 
  22.  
  23.             Stopwatch = new Stopwatch(); 
  24.             Stopwatch.Start(); 
  25.         } 

2、定义Stopwatch ,计算方法的耗时。

 
 
 
  1. private string ActionArguments { get; set; } 
  2.  
  3.         ///  
  4.         /// 请求体中的所有值 
  5.         ///  
  6.         private string RequestBody { get; set; } 
  7.  
  8.         private Stopwatch Stopwatch { get; set; } 

3、结束的时候,把信息打印出来OnActionExecuted。

 
 
 
  1. public override void OnActionExecuted(ActionExecutedContext context) 
  2.         { 
  3.             base.OnActionExecuted(context); 
  4.             Stopwatch.Stop(); 
  5.  
  6.             string url = context.HttpContext.Request.Host + context.HttpContext.Request.Path + context.HttpContext.Request.QueryString; 
  7.             string method = context.HttpContext.Request.Method; 
  8.             string controller = context.Controller.ToString(); 
  9.             string action = context.ActionDescriptor.DisplayName; 
  10.             string token = ""; 
  11.             if (context.HttpContext.Request != null && context.HttpContext.Request.Headers != null && context.HttpContext.Request.Headers["Authorization"].Count > 0) 
  12.             { 
  13.                 token = context.HttpContext.Request.Headers["Authorization"]; 
  14.             } 
  15.             string qs = ActionArguments; 
  16.             dynamic result = context?.Result?.GetType()?.Name == "EmptyResult" ? new { Value = "无返回结果" } : context?.Result as dynamic; 
  17.  
  18.             string res = "在返回结果前发生了异常"; 
  19.             try 
  20.             { 
  21.                 if (result != null) 
  22.                 { 
  23.                     res = Newtonsoft.Json.JsonConvert.SerializeObject(result.Value); 
  24.                 } 
  25.             } 
  26.             catch (System.Exception) 
  27.             { 
  28.                 res = "日志未获取到结果,返回的数据无法序列化"; 
  29.             } 
  30.  
  31.             NLogger.Info( 
  32.                 $"地址:{url} \n " + 
  33.                   $"controller:{controller} \n " + 
  34.                     $"action:{action} \n " + 
  35.                       $"token:{token} \n " + 
  36.                 $"方式:{method} \n " + 
  37.                 $"请求体:{RequestBody} \n " + 
  38.                 $"参数:{qs}\n " + 
  39.                 $"结果:{res}\n " + 
  40.                 $"耗时:{Stopwatch.Elapsed.TotalMilliseconds} 毫秒(指控制器内对应方法执行完毕的时间)"); 
  41.         } 

4、控制器调用LogAttribute。

 
 
 
  1. ///  
  2.     /// 
  3.     ///  
  4.     [Produces("application/json")] 
  5.     [LogAttribute] 
  6.     [CustomExceptionFilterAttribute] 
  7.     public class DefaultController : Controller 
  8.     { 
  9.     } 

 

完整代码



 
 
 
  1. using CompanyName.ProjectName.Core; 
  2. using Microsoft.AspNetCore.Mvc.Filters; 
  3. using System.Diagnostics; 
  4.  
  5. namespace CompanyName.ProjectName.HttpApi.Host.Code 
  6. { 
  7.     ///  
  8.     /// 拦截器 
  9.     ///  
  10.     public class LogAttribute : ActionFilterAttribute 
  11.     { 
  12.         private string ActionArguments { get; set; } 
  13.  
  14.         ///  
  15.         /// 请求体中的所有值 
  16.         ///  
  17.         private string RequestBody { get; set; } 
  18.  
  19.         private Stopwatch Stopwatch { get; set; } 
  20.  
  21.         ///  
  22.         /// 
  23.         ///  
  24.         ///  
  25.         public override void OnActionExecuting(ActionExecutingContext context) 
  26.         { 
  27.             base.OnActionExecuting(context); 
  28.  
  29.             // 后续添加了获取请求的请求体,如果在实际项目中不需要删除即可 
  30.             long contentLen = context.HttpContext.Request.ContentLength == null ? 0 : context.HttpContext.Request.ContentLength.Value; 
  31.             if (contentLen > 0) 
  32.             { 
  33.                 // 读取请求体中所有内容 
  34.                 System.IO.Stream stream = context.HttpContext.Request.Body; 
  35.                 if (context.HttpContext.Request.Method == "POST") 
  36.                 { 
  37.                     stream.Position = 0; 
  38.                 } 
  39.                 byte[] buffer = new byte[contentLen]; 
  40.                 stream.Read(buffer, 0, buffer.Length); 
  41.                 // 转化为字符串 
  42.                 RequestBody = System.Text.Encoding.UTF8.GetString(buffer); 
  43.             } 
  44.  
  45.             ActionArguments = Newtonsoft.Json.JsonConvert.SerializeObject(context.ActionArguments); 
  46.  
  47.             Stopwatch = new Stopwatch(); 
  48.             Stopwatch.Start(); 
  49.         } 
  50.  
  51.         ///  
  52.         /// 
  53.         ///  
  54.         ///  
  55.         public override void OnActionExecuted(ActionExecutedContext context) 
  56.         { 
  57.             base.OnActionExecuted(context); 
  58.             Stopwatch.Stop(); 
  59.  
  60.             string url = context.HttpContext.Request.Host + context.HttpContext.Request.Path + context.HttpContext.Request.QueryString; 
  61.             string method = context.HttpContext.Request.Method; 
  62.             string controller = context.Controller.ToString(); 
  63.             string action = context.ActionDescriptor.DisplayName; 
  64.             string token = ""; 
  65.             if (context.HttpContext.Request != null && context.HttpContext.Request.Headers != null && context.HttpContext.Request.Headers["Authorization"].Count > 0) 
  66.             { 
  67.                 token = context.HttpContext.Request.Headers["Authorization"]; 
  68.             } 
  69.             string qs = ActionArguments; 
  70.             dynamic result = context?.Result?.GetType()?.Name == "EmptyResult" ? new { Value = "无返回结果" } : context?.Result as dynamic; 
  71.  
  72.             string res = "在返回结果前发生了异常"; 
  73.             try 
  74.             { 
  75.                 if (result != null) 
  76.                 { 
  77.                     res = Newtonsoft.Json.JsonConvert.SerializeObject(result.Value); 
  78.                 } 
  79.             } 
  80.             catch (System.Exception) 
  81.             { 
  82.                 res = "日志未获取到结果,返回的数据无法序列化"; 
  83.             } 
  84.  
  85.             NLogger.Info( 
  86.                 $"地址:{url} \n " + 
  87.                   $"controller:{controller} \n " + 
  88.                     $"action:{action} \n " + 
  89.                       $"token:{token} \n " + 
  90.                 $"方式:{method} \n " + 
  91.                 $"请求体:{RequestBody} \n " + 
  92.                 $"参数:{qs}\n " + 
  93.                 $"结果:{res}\n " + 
  94.                 $"耗时:{Stopwatch.Elapsed.TotalMilliseconds} 毫秒(指控制器内对应方法执行完毕的时间)"); 
  95.         } 
  96.     } 
  97. } 

分享文章:巧用ActionFilterAttribute实现API日志的记录
文章出自:http://www.jxruijie.cn/article/dheppip.html