我们通过在Controller上加Filter实现控制器下所有操作都应用过滤器,但其中个别操作不想应用过滤器怎么办?上篇我们通过定义一个空过滤器,在执行的时候进行判断,但每排除一个就需要写一个空过滤器,这样判断太麻烦太臃肿,而且容易出错。现在提供一个一劳永逸又方便的解决方案,IFilterProvider。

IFilterProvider起到控制所有过滤器的作用,如下

webapifilterprovider
  • 假设我们有一个授权验证过滤器
    [AttributeUsage( AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true )]
    public class VerifyTokenAttribute : System.Web.Http.Filters.ActionFilterAttribute
    {
        public override void OnActionExecuting( HttpActionContext actionContext )
        {
            //do your verify

            base.OnActionExecuting( actionContext );
        }
    }
  • 我们写一个特殊的过滤器,实现排除其他过滤器的功能(别忘了在App_Start/WebApiConfig.cs中添加)
    [AttributeUsage( AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true )]
    public class ExcludeFilterAttribute : System.Web.Http.Filters.ActionFilterAttribute
    {
        private Type filterType;

        public ExcludeFilterAttribute( Type filterType )
        {
            this.filterType = filterType;
        }

        public Type FilterType
        {
            get
            {
                return this.filterType;
            }
        }
    }
  • 下边整个Controller都会应用VerifyTokenAttribute,在我们想排除的操作上加Exclude过滤器
    [VerifyToken]
    public class MsgController : ApiController
    {
        [HttpPost]
        public MsgGetListResult GetMsgList( BaseRequest model )
        {
            //会走VerifyTokenAttribute过滤器
        }

        [ExcludeFilter( typeof( VerifyTokenAttribute ) )]
        [HttpPost]
        public MsgGetListResult notverify( string a )
        {
            //排除VerifyTokenAttribute过滤器
        }

    }
  • 要想实现这种效果,我们实现一个自己的IFilterProvider,接口有一个GetFilters方法,返回自己需要应用的过滤器集合
    public class ExcludeFilterProvider : IFilterProvider
    {
        public IEnumerable<FilterInfo> GetFilters( HttpConfiguration configuration, HttpActionDescriptor actionDescriptor )
        {
            //应用在Action上的过滤器
            var actionFilters = actionDescriptor.GetFilters();
            //应用在Controller上的过滤器
            var controllerFilters = actionDescriptor.ControllerDescriptor.GetFilters();

            var allFilters = actionFilters.Concat( controllerFilters );

            IEnumerable<ExcludeFilterAttribute> excludeFilters
                = ( from f in allFilters
                    where f is ExcludeFilterAttribute
                    select f as ExcludeFilterAttribute );

            List<Type> filterTypesToRemove = new List<Type>();
            foreach ( ExcludeFilterAttribute excludeFilter in excludeFilters )
            {
                filterTypesToRemove.Add( excludeFilter.FilterType );
            }

            IEnumerable<FilterInfo> result
                = ( from f in allFilters
                    where !filterTypesToRemove.Contains( f.GetType() )
                    select new FilterInfo( f, FilterScope.Action ) );

            return result;
        }
    }
  • 最后我们需要在App_Start/WebApiConfig.cs替换默认的FilterProvider
    public static class WebApiConfig
    {
        public static void Register( HttpConfiguration config )
        {
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            #region Add Filters

            config.Filters.Add( new VerifyTokenAttribute() );
            config.Filters.Add( new ExcludeFilterAttribute( null ) );

            #endregion


            #region Set FilterProvider

            var providers = config.Services.GetFilterProviders();

            var defaultprovider = providers.Single( i => i is ActionDescriptorFilterProvider );
            config.Services.Remove( typeof( IFilterProvider ), defaultprovider );

            var configprovider = providers.Single( i => i is ConfigurationFilterProvider );
            config.Services.Remove( typeof( IFilterProvider ), configprovider );

            config.Services.Add( typeof( IFilterProvider ), new ExcludeFilterProvider() );

            #endregion

        }
    }

以后想排除过滤器,只需加上[ExcludeFilter( typeof( 要排除的过滤器 ) )]