• Asp.Net MVC Filter 实现方式和作用范围控制


          MVC中的Filte 简单又优雅的实现了AOP ,在日志,权限,缓存和异常处理等方面用的比较多。但本文不是讨论Filter这些功能点,而是总结Filter实现的方式。说实现也不太准确,也就是它的呈现方式。自带有四种Filter(借用了Liam wang的图)

          

          如果要实现自己的Filter,根本还是在于是实现第二项的接口。

          实现方式:

    一、特性式

          从上图可以看到,Filter的默认实现方式就是带有Attribute后缀的,有了Attribute,我们就可以将我们的Filter像标签一样的贴在方法或者控制器的上方。这样直观又简洁。最简单的自定义就是继承默认Filter。比如我定义一个LogFilter,继承于ActionFilter记录方法名,参数,和备注信息。

       public class LogFilterAttribute : ActionFilterAttribute
        {
            public string Message { get; set; }
            public override void OnActionExecuting(ActionExecutingContext filterContext)
            {
                var cname = filterContext.RouteData.Values["controller"].ToString();
                var actionName = filterContext.RouteData.Values["action"].ToString();
                var str = filterContext.ActionParameters.Aggregate("", (current, actionParameter) => current + (actionParameter.Key + ":" + actionParameter.Value));
                var message = string.Format("控制器:{0} 方法:{1} 参数:{2},执行时间:{3},备注信息:{4}", cname, actionName, str, DateTime.Now,
                    Message);
                Logger.Debug(message);
            }
            public override void OnResultExecuted(ResultExecutedContext filterContext)
            {
            }
        }

     调用的时候,贴在指定的方法上面就可以了。(当然你命名的时候可以把“Filter”拿掉),当然也可以分别去继承接口 比如public class MyLogAttribute : FilterAttribute, IActionFilter

      [LogFilter(Message = "管理员新增产品")]
      public ActionResult Create(Product product){
      //..... 
     }

    二、控制器实现

      查看我们的Controller类,它本身是继承于IActionFilter 等这些接口的,同样,我们在控制器里面实现了这些方法也能发挥Filter的作用。没了Attribute,继续AOP

     

      public class UserController : Controller
        {
           //这里实现一个方法  
    protected override void OnActionExecuting(ActionExecutingContext filterContext) { filterContext.HttpContext.Response.Write("控制器实现,Action正在执行"); }
    //..... }

     这样在当前控制器下的所有方法执行时,页面都会输出这样一句话。

    这种做法的作用不大,可以将前控制器作为一个控制器基类。  

    三、依赖注入式

       这种方式意义不大,感觉绕了大弯子,而且不灵活。权当熟悉一下Ninject。

       1.创建接口并实现。(IOC总是离不开接口)

      public interface IMessageProvider
        {
            string Message { get; }
        }
    
        public class SimpleMessageProvider : IMessageProvider
        {
            public string Message
            {
                get
                {
                    return "Hello IOC";
                }
            }
        }
    View Code

    2.实现一个自定义Filter

    public class DIMessageAttribute : FilterAttribute, IActionFilter {
         
            [Inject]//告诉我们的DI容器 这个属性需要依赖注入来获取实例
            public IMessageProvider Provider { get; set; }
            
           public void OnActionExecuting(ActionExecutingContext filterContext)
           {
              filterContext.HttpContext.Response.Write(string.Format("[Before Action:{0}]",Provider.Message));
           }
    
           public void OnActionExecuted(ActionExecutedContext filterContext)
           {
               filterContext.HttpContext.Response.Write(string.Format("[After Action:{0}]", Provider.Message));
           }
       }
    View Code

    3.需要定义一个Filter Provider 在获取到上面类型的Filter的时候进行处理。

     public class DIFilterProvider : FilterAttributeFilterProvider
        {
            public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext,
                ActionDescriptor actionDescriptor)
            {
                IEnumerable<Filter> filters = base.GetFilters(controllerContext,
                    actionDescriptor);
                var dependencyResolver = DependencyResolver.Current as NinjectDependencyResolver;
                 
                if (dependencyResolver != null)
                {
                    foreach (Filter f in filters)
                    {
                        dependencyResolver.Kernel.Inject(f.Instance);
                    }
                    //Inject 方法会让Ninject去检查我们传入的对象,当它发现了Inject特性,就会为那个属性创造一个实例 前面的message
                }
                return filters;
            }
        }

    4.绑定接口并加入Provider

     我们需要在NinjectDependencyResolver 中绑定IMessageProvider和SimpleMessageProvider

     public class NinjectDependencyResolver : IDependencyResolver
        {
            private readonly IKernel _kernel;
    
            public NinjectDependencyResolver()
            {
                _kernel = new StandardKernel();
                AddBindings();
            }
    
            public object GetService(Type serviceType)
            {
                return _kernel.TryGet(serviceType);
            }
    
            public IEnumerable<object> GetServices(Type serviceType)
            {
                return _kernel.GetAll(serviceType);
            }
            public IBindingToSyntax<T> Bind<T>()
            {
                return _kernel.Bind<T>();
            }
    
            public IKernel Kernel
            {
                get { return _kernel; }
            }
    
            private void AddBindings()
            {
                _kernel.Bind<IUserRepository>().To<UserRepository>();
                _kernel.Bind<IDownloadFileRepository>().To<DownloadRepository>();
                _kernel.Bind<IAuthProvider>().To<FormsAuthProvider>();
              
                //绑定
                Bind<IMessageProvider>().To<SimpleMessageProvider>();
            }
        }
    View Code

    在Global中加人

    protected void Application_Start() {
    AreaRegistration.RegisterAllAreas();
    DependencyResolver.SetResolver(new NinjectDependencyResolver());
    
    FilterProviders.Providers.Insert(0,new DIFilterProvider()); //插在第一个。(这样便于实现需要IOC的Filter,不然其他的Provider找到Filter就返回了)
    
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
    }

    这样在方法前面加入[DIMessage] 即可

     用的最多的还是继承默认Filter和继承接口实现,他们的作用范围除了可以通过直接“贴”在Action或者Controller上面来控制还可以通过全局注册和Provider的方式来控制。

    一、全局注册

       在App_Start 文件夹中的FilterConfig已经默认注册了一个全局的 HandleErrorAttribute,这样相当于给所有的方法加上了HandleError标签

     public class FilterConfig
        {
            public static void RegisterGlobalFilters(GlobalFilterCollection filters)
            {
                filters.Add(new HandleErrorAttribute()
                {
                    ExceptionType = typeof(NullReferenceException),//可以设置捕获的异常类型
                    View = "SpecialError"//默认跳转的视图
                });
            }
        }

    二、FilterProvider

      但如果我不想全局注册,也不想一个一个去加在方法的上方,我想一次性给所有控制器的Index方法加一个Filter,那怎么办? 用Filter provider 。

      从IFilterProvider接口开始

    public interface IFilterProvider {
    IEnumerable<Filter> GetFilters(ControllerContext controllerContext,
    ActionDescriptor actionDescriptor);
    }

     实现了这个接口并注册,在触发一个Action时候,GetFilters就会被调用。 

     而上文的Filter类是这样的,它包装了filter对象,而真正执行的filter就是这个instance。

    namespace System.Web.Mvc {public class Filter {
    public const int DefaultOrder = -1;
    public Filter(object instance, FilterScope scope, int? order) {
    // ...statements removed for clarity...
    }
    public object Instance { get; protected set; }
    public FilterScope Scope { get; protected set;}
    public int Order { get; protected set; }
    }
    }

    Order和Scope定义了这个Filter的优先级和范围。FilterScope是枚举类型,包含First,Global,Controller,Action,Last。

    我们先定义一个CustomFilterWrapper ,继承Filter,让其支持lambda 便于选择过滤。

     public class CustomFilterWrapper : Filter
      {
         public CustomFilterWrapper(object instance, FilterScope scope, int? order,Func<ControllerContext,ActionDescriptor,bool> selector ) : base(instance, scope, order)
            {
                Selector = selector;
            }
    
            public Func<ControllerContext, ActionDescriptor, bool> Selector//便于通过控制器名称和action名称来过滤
            {
                get;
                set;
            }
    }

    我们再实现一个CustomFilterProvider  

     public class CustomFilterProvider : IFilterProvider
        {
            private IList<CustomFilterWrapper> wrappers;
    
            public CustomFilterProvider()
            {
                wrappers = new List<CustomFilterWrapper>();
            }
            public IList<CustomFilterWrapper> Wrappers
            {
                get { return wrappers; }
            }
    
            public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
            {
                return wrappers.Where(e => e.Selector(controllerContext, actionDescriptor));
            }
        }

    这样我们就可以Global中注册了。这样我们可以通过LINQ来选择我们需要加入Filter的方法了。

    protected void Application_Start() {
    AreaRegistration.RegisterAllAreas();
    
    CustomFilterProvider customFilterProvider
    = new CustomFilterProvider(); customFilterProvider.Wrappers.Add(new CustomFilterWrapper(new LogFilterAttribute(), FilterScope.Action, 1, (c, a) => a.ActionName == "Index"));//所有控制器的Action方法会触发Logfilter
    FilterProviders.Providers.Add(customFilterProvider);
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
    }

     特此分享,希望对你有帮助,Thanks!

     参考:Pro Asp.Net MVC3 FrameWork 

  • 相关阅读:
    Java随笔
    Java随笔
    Java随笔
    CF1271D Portals(反悔贪心)
    CF938D Buy a Ticket(最短路)
    CF1117C Magic Ship(二分)
    HDU6820 Tree(树形dp)
    P2393 美味(主席树+贪心)
    HDU6831 Fragrant numbers(区间dp)
    HDU6832 A Very Easy Graph Problem(生成树)
  • 原文地址:https://www.cnblogs.com/stoneniqiu/p/3654851.html
Copyright © 2020-2023  润新知