自定义动作过滤器属性

在新的项目里,需要实现记录log的功能,本来想套用原有的Log4Net或者企业库中的logging功能,可总觉得在MVC框架里用的别扭,后来查资料,发现可以通过自定义过滤器属性达到目的。

MVC的动作过滤器机制详细说明
过滤器是一组.NET特性,MVC在特定运行时点调用这些特性上的指定方法,以此实现功能注入。MVC包含四个基本的过滤器类型:授权 (Authorization)、活动(Action)、结果(Result)以及异常(Exception)。MVC为这四中过滤器提供了接口定 义:IAuzhorizationFilter、IActionFilter、IResultFilter、IExceptionFilter,所以 MVC在运行时知道如何调用过滤器上的方法。
    MVC实现的默认过滤器如以下类图所示:



    注意上图中Controller实现了四个基本的过滤器接口,所以在我们的控制器中,可直接重写过滤器方法来实现过滤器功能,此种方式适用于过滤器功能特定于控制器的情景,如果过滤器功能会跨多个控制器使用,那么使用特性的方式可避免重复代码。

    各种默认过滤器,都直接或间接的继承于FilterAttribute类,FilterAttribute有一个Order属性,用于过滤器的排序。另 外,MVC为Action过滤器和Result过滤器提供了一个抽象基类ActionFilterAttribute,它同时实现了 IActionFilter和IResultFilter接口。

    过滤器的执行过程由控制器的ActionInvoker对象实现,他是一个实现了IActionInvoker接口的类,ActionInvoker是控 制器的一个公共属性,所以我们可以实现自己的IActionInvoker类,然后通过设定Controller的ActionInvoker属性来指定 自定义的Invoker。MVC中默认的ActionInvoker类为ControllerActionInvoker,其执行过滤器列表的过程如下图 所示:


    上图表示的是过滤器在没有发生任何异常时的执行顺序:获取过滤器列表——>依次调用按Order排序的授权过滤器,如果某个授权过滤器设置了 AuthorizationContext参数的Result属性,则立即终止剩余授权过滤器的调用,直接执行Result,生成页面应答内容。如果所有 授权过滤器都通过,并且Result为空,则继续调用Action过滤器上的OnActionExecuting方法,如果全部通过,将执行控制器上相应 的Action方法获取ActionResult对象,随后再按相反顺序执行Action过滤器上的OnActionExecuted方法,之后转入到 Result过滤器列表中,同Action类似,先按Order顺序执行OnResutExecuting方法,注意在 OnResultExecuting方法上,过滤器可通过将ResultExecutingContext.Cancel属性设置为True来立即终止 Invoker的执行(即不会执行ActionResult及Result过滤器上的OnResultExecuted方法),如果所有的 OnResultExecuting方法都通过,则通过ActionResult.ExecuteResult方法生成应答内容,最后再按相反顺序调用 Result过滤器上的OnResultExecuted方法。

    对于ControllerActionInvoker类在执行过程中产生的异常,会传给Exception过滤器(MVC默认的异常处理过滤器为 HandleErrorAttribute)来处理。在ControllerActionInvoker的各个节点产生的异常会影响到过滤器的执行过程, 下面分几种情况来详细了解:

   我们假设某个Action上有A、B两个授权过滤器,有A、B、C三个Action过滤器和Result过滤器以及A、B两个异常过滤器,首先,在没有异常发生时得执行过程如下图:A、B两个异常过滤器未执行,其余所有过滤器都执行


  如果在B.OnAuthorization上发生异常,会直接转到异常过滤器上,其余Action过滤器、Result过滤器及Action方法都不会调用:


   如果在B.OnActionExecuting上发生异常,则会跳转到Action过滤器列表的上一个过滤器(即A),执行A.OnActionExecuted,然后转到异常过滤器:


    如果在B.OnActionExecuting上发生异常,但是在A.OnActionExecuted中将ActionExecutedContext.ExceptionHandled设置为true,此时将会继续执行Result过滤器,并忽略异常过滤器:


   如果在执行控制器的Action方法时发生异常,将会继续执行ActionExecuted方法,随后转到异常过滤器:


    如果在Action的OnActionExecuted方法上发生异常,将会继续执行完Action过滤器,然后转到异常过滤器:

    在Result过滤器上的OnResultExecuting方法和OnResultExecuted方法上发生异常,其处理方式与Action过滤器类似,此处不再详细说明。

    如果在Result过滤器OnResultExecuting中将ResultExecutingContext的Cancel属性设置为true,将立即完成Action方法执行:


   另外需要注意,在异常过滤器中将ExceptionContext.ExceptionHandled设置为true并不会终止异常过滤器的执行,该属性 仅仅用于标记“异常已被处理”。所以我们可以通过异常过滤器来实现错误日志记录功能,无需担心漏掉"已被处理"的异常。

    如果我们要实现自定义的权限验证功能,应该从AuthorizeAttribute类继承,原因在于AuthorizeAttribute已经优化了与OutputCacheAttribute缓存过滤器的协调,避免因缓存原因造成权限验证失效的情况。

自定义动作过滤器如下

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public class LogMessageAttribute : FilterAttribute, IActionFilter, IResultFilter, IExceptionFilter
    {
        /// <summary>
        /// <param name="LogName ">日志文件路径</para>
        /// </summary>
        public string LogName
        {
            get;
            set;
        }

        public LogMessageAttribute()
        {
           string webPath = HttpContext.Current.Server.MapPath("/");
           bool hasLogFile = false;
           LogName = webPath + "Log.log";
           if (File.Exists(LogName))
           {
               FileInfo file = new FileInfo(LogName);
               if (file.CreationTime.Date != DateTime.Now.Date)
               {
                   file.MoveTo(webPath + file.CreationTime.Date.ToString("yyyyMMdd", new CultureInfo("en-US")) + ".log");
                   hasLogFile = false;
               }
               else
               {
                   hasLogFile = true;
               }
           }
           if (!hasLogFile)
           {
               File.Create(LogName);
           }
        }
        /// <summary>
        /// 记录时间,系统版本,当前线程ID 等记录
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="action"></param>
        /// <param name="message"></param>
        public void LogMessage(string controller, string action, string message)
        {

            if (!string.IsNullOrEmpty(LogName))
            {
                TextWriter writer = new StreamWriter(LogName, true);

                writer.WriteLine("################# Begin #################");
                writer.WriteLine("Time:[{0}]", DateTime.Now.ToString("yyyy-MM-dd- hh:mm:ss"));
                writer.WriteLine("Controller:{0}", controller);
                writer.WriteLine("Action:{0}", action);
                writer.WriteLine("Message:{0}", message);
                writer.WriteLine("############### Over ###############");

                writer.Close();
            }

        }

        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            LogMessage(filterContext.RouteData.Values["controller"].ToString(),
            filterContext.RouteData.Values["action"].ToString(),
            "Action exeuting...");
        }

        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            LogMessage(filterContext.RouteData.Values["controller"].ToString(),
            filterContext.RouteData.Values["action"].ToString(),
            "Action executed.");
        }

        public void OnResultExecuting(ResultExecutingContext filterContext)
        {
            LogMessage(filterContext.RouteData.Values["controller"].ToString(),
            filterContext.RouteData.Values["action"].ToString(),
            "Result executing...");
        }

        public void OnResultExecuted(ResultExecutedContext filterContext)
        {
            LogMessage(filterContext.RouteData.Values["controller"].ToString(),
            filterContext.RouteData.Values["action"].ToString(),
            "Result executed");
        }
        public void OnException(ExceptionContext filterContext)
        {
            LogMessage(filterContext.RouteData.Values["controller"].ToString(),
            filterContext.RouteData.Values["action"].ToString(),
            filterContext.Exception.Message);
        }
    }

 

 

在Controller中使用:

    [HandleError]
    [LogRequest]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewData["Title"] = "Home Page";
            ViewData["Message"] = "Welcome to ASP.NET MVC!";

            return View();
        }

        public ActionResult About()
        {
            ViewData["Title"] = "About Page";

            return View();
        }
    }

 




原文地址:https://www.cnblogs.com/yumianhu/p/3710766.html