zoukankan      html  css  js  c++  java
  • 使用ActionFilterAttribute 记录 WebApi Action 请求和返回结果记录

    转载:https://www.cnblogs.com/hnsongbiao/p/7039666.html

    在asp.net mvc 中 webapi 和 mvc 处理消息是两个不同的管道,Asp.net mvc 和 webapi 为我们提供的 ActionFilterAttribute 拦截器,通过 重写 OnActionExecutingAsync,来 拦截action的请求消息,当执行OnActionExecutingAsync完成以后才真正进入请求的action中,action运行完后又把控制权给了 OnActionExecutedAsync ,这个管道机制可以使我们用它来轻松实现 权限认证、日志记录 ,跨域以及很多需要对全局或者部分请求做手脚的的功能。

    大概的流程如下

    通过ActionFilterAttribute ,就能拦截action 处理的所有内容,包括请求提交的参数以及返回值。由于asp.net MVC 与webapi  是两个完全独立的管道:

    • MVC由System.Web.Mvc.ActionFilterAttribute 来做action请求的拦截。
    • webapi 由 System.Web.Http.Filters.ActionFilterAttribute 来处理。

    因此拦截action请求是完全不相干的两个通道,于此同时,当我们需要注册全局的ActionFilterAttribute  这两个也是分开注册的:

    MVC 直接在System.Web.Mvc.GlobalFilterCollection  这个全局管道里面注册 ActionFilter ,位置在App_Start目录>FilterConfig 类>RegisterGlobalFilters 方法 使用参数filters , filters.Add(new YourMvcAttribute()) 添加你的mvc ActionFilterAttribute  。

    wepi API 在System.Web.Http.Filters 中注册, 在项目的App_Start 目录>WebApiConfig类中>Register 方法中加入使用 config参数, config.Filters.Add(new YourWebApiAttribute()); 添加你的 webapi ActionFilterAttribute 

    这样就可以注册你的 ActionFilterAttribute   成为全局的Filter,系统中请求经过Action 之前或之后 都会被你的ActionFilter 拦下来做处理然后在转交下去。

    好了道理已经讲完了,现在开始我自己要实现的 日志记录功能,

    需求是记录所有访问webapi action的(请求地址、内容、访问用户、提交的参数、返回的结果、以及一些客户端的信息)

    由于MVC 框架 提倡契约编程,在你自定义的Attribute 时,需要遵守契约规范, 【YourFilterName】+Attribute ,所以我的filter名字为 OperateTrackAttribute

    复制代码
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web;
    using System.Web.Http.Controllers;
     using System.Web.Http.Filters;
     using WebApiTrackLog.Models;
    
     
     namespace WebApiTrackLog.WebApiAttributes
     {
         public class OperateTrackAttribute : ActionFilterAttribute
         {
             /// <summary>
             /// 自定义参数
             /// </summary>
             public string msg { get; set; }
             public OperateTrackAttribute()
             {
     
             }
     
     
             /// <summary>
             /// 初始化时填入类的说明
             /// </summary>
             /// <param name="message"></param>
             public OperateTrackAttribute(string message)
             {
                 msg = message;
             }
     
     
             private static readonly string key = "enterTime";
             public override Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
             {
                 if (SkipLogging(actionContext))//是否该类标记为NoLog
                 {
                     return base.OnActionExecutingAsync(actionContext, cancellationToken);
     
                 }
                 //记录进入请求的时间
                 actionContext.Request.Properties[key] = DateTime.Now.ToBinary();
     
                 return base.OnActionExecutingAsync(actionContext, cancellationToken);
             }
             /// <summary>
             /// 在请求执行完后 记录请求的数据以及返回数据
             /// </summary>
             /// <param name="actionExecutedContext"></param>
             /// <param name="cancellationToken"></param>
             /// <returns></returns>
             public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
             {
                 object beginTime = null;
                 if (actionExecutedContext.Request.Properties.TryGetValue(key, out beginTime))
                 {
                     DateTime time = DateTime.FromBinary(Convert.ToInt64(beginTime));
                     HttpRequest request = HttpContext.Current.Request;
                     string token = request.Headers["token"];
     
                     WepApiActionLog apiActionLog = new WepApiActionLog
                     {
                         Id = Guid.NewGuid(),
                         //获取action名称
                         actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName,
                         //获取Controller 名称
                         controllerName = actionExecutedContext.ActionContext.ActionDescriptor.ControllerDescriptor.ControllerName,
                         //获取action开始执行的时间
                         enterTime = time,
                         //获取执行action的耗时
                         costTime = (DateTime.Now - time).TotalMilliseconds,
                         navigator = request.UserAgent,
                         token = token,
                         //获取用户token
                         userId = getUserByToken(token),
                         //获取访问的ip
                         ip = request.UserHostAddress,
                         userHostName = request.UserHostName,
                         urlReferrer = request.UrlReferrer != null ? request.UrlReferrer.AbsoluteUri : "",
                         browser = request.Browser.Browser + " - " + request.Browser.Version + " - " + request.Browser.Type,
                         //获取request提交的参数
                         paramaters = GetRequestValues(actionExecutedContext), 88                     //获取response响应的结果
                         executeResult = GetResponseValues(actionExecutedContext),
                         comments = msg,
                         RequestUri = request.Url.AbsoluteUri
                     };
                     using (TrackLogEntities context = new TrackLogEntities())
                     {
                        context.WepApiActionLogs.Add(apiActionLog);
                        context.SaveChanges();
                    }
                }
                return base.OnActionExecutedAsync(actionExecutedContext, cancellationToken);
     
             }
             /// <summary>
             /// 获取当前登录用户的id
             /// </summary>
             /// <param name="token"></param>
             /// <returns></returns>
             public static int getUserByToken(string token)
             {
                 UserInfo user = null;
                 // TokenManager.getUserByToken(token, out user);
                 return user == null ? 0 : user.user_id;
             }
             /// <summary>
             /// 读取request 的提交内容
             /// </summary>
             /// <param name="actionExecutedContext"></param>
             /// <returns></returns>
             public string GetRequestValues(HttpActionExecutedContext actionExecutedContext)
             {
     
                 Stream stream = actionExecutedContext.Request.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>
             /// 读取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>
             /// 判断类和方法头上的特性是否要进行Action拦截
             /// </summary>
             /// <param name="actionContext"></param>
             /// <returns></returns>
             private static bool SkipLogging(HttpActionContext actionContext)
             {
                 return actionContext.ActionDescriptor.GetCustomAttributes<NoLogAttribute>().Any() || actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<NoLogAttribute>().Any();
             }
     
         }
     
     }
    复制代码

    如果将webapi 的 OperateTrackAttribute 注册为webapi全局的 ActionFilter 那么我们如果有不想过滤的action 时,可以通过 检查 方法或类顶部特性   来对那些不需要接受拦击的 Controller 和action 顶部添加一个这样的特性来区分开,并通过在filter中检查是被拦截的action或controller 否包含此特性标记,不包含时拦截。

    下面是这个类的写法,一个空的类 继承Attribute,并在类顶部写出该Attribute 使用的范围

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]
        public class NoLogAttribute : Attribute
        {
        }

    这样我们的拦截就更灵活了,无论是添加了整个个Controller 的拦截还是全局拦截,只需要在不拦截的 controller 或action头部加上 [NoLog] 

    复制代码
    例如
    
    /// <summary>
    /// 记录该类中的Action内容
    /// </summary>
    [OperateTrack]
    public class TestApiLogController : ApiController
    {
        [HttpPost]
        public object Login(UserInfo user)
        {
            var result = new { data = user, status = true };
            return result;
        }
        /// <summary>
        /// 该类不参与记录
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        [NoLog]
        public string DontLogMe(string name)
        {
            return name;
        }
    }
    或者
    
    /// <summary>
    /// 该Controller 下的所有action 都不会被全局的OperateTrack Filter 拦截
    /// </summary>
    [NoLog]
    public class UserManagerController : ApiController
    {
        public List<string> GetUsers() {
            return new List<string>() { "tomers","jack"};
        }
        public  string GiveUserSomeMoney(int money)
        {
            return money+"";
        }
    }
    复制代码

    我们来测试一下: 提交到/api/TestApiLog/Login  整个action 被标记为拦截

    再看看记录的结果,结果已经记录了

    这样整个记录用户访问记录的拦截器就到此为止了。

  • 相关阅读:
    luogu 1865 数论 线性素数筛法
    洛谷 2921 记忆化搜索 tarjan 基环外向树
    洛谷 1052 dp 状态压缩
    洛谷 1156 dp
    洛谷 1063 dp 区间dp
    洛谷 2409 dp 月赛题目
    洛谷1199 简单博弈 贪心
    洛谷1417 烹调方案 dp 贪心
    洛谷1387 二维dp 不是特别简略的题解 智商题
    2016 10 28考试 dp 乱搞 树状数组
  • 原文地址:https://www.cnblogs.com/qtiger/p/13427670.html
Copyright © 2011-2022 走看看