实现MVC自定义过滤器,自定义Area过滤器,自定义Controller,Action甚至是ViewData过滤器
MVC开发中几种以AOP方式实现的Filters是非常好用的,默认情况下,我们通过App_Start中的FilterConfig来实现的过滤器注册是全局的,也就是整个应用程序都会使用的,针对单独的Filter我们不得不去单独的Controller或者Action去定义
如图:
那么问题来了,我现在想在FitlerConfig里面去维护所有的过滤器,但是又想实现自定义的过滤器该咋搞,MVC默认不支持!
我们先来看看,MVC默认的Fitlers注册是怎样的
官方源码:GlobalFilterCollection.cs
GlobalFilters.cs
再看看,App_Start里面的FilterConfig.cs
可以发现,其实是GlobalFilters里面定义了静态的GlobalFIlterCollection对象,然后通过FilterConfig像这个静态的集合注册Filters,那么我们没看到MVC哪调用了这个集合,是的,Global里面是没有明确写出来,但是我们来看看FilterProviders.cs的源码
在静态的构造函数中就已经通过Providers.Add(GlobalFilters.Filters);把GlobalFilters.Filters集合给添加进去了,然后MVC每次请求会调用IFilterProvider.GetFilters,那为什么自带的GlobalFilterCollection就不能实现自定义的过滤器呢,请看最上面GlobalFilterCollection.cs的GetFilters代码,他直接返回所有的Filters,而且这个类不能重写,好吧!我们来自己写一个实现自定义过滤器!
可以完全照搬GlobalFilterCollection.cs,GlobalFilters.cs代码,然后根据自己的需求改改
这里我们要通过GetFilters实现自己的筛选规则
看看原始方法源码:
1
2
3
4
5
|
IEnumerable<Filter> IFilterProvider.GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
return this ;
}
|
这个方法传进来两个参数:ControllerContext和ActionDescriptor
我们添加Add方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/// <summary> /// 注册注册局部规则过滤器
/// </summary>
/// <param name="func"></param>
/// <param name="filter"></param>
/// <param name="order"></param>
public void Add(Func<ControllerContext, ActionDescriptor, bool > func, object filter, int order)
{
Add(func, filter, order);
}
private void Add(Func<ControllerContext, ActionDescriptor, bool > func, object filter, int ? order)
{
ValidateFilterInstance(filter);
items.Add( new FilterItem
{
filter = new Filter(filter, FilterScope.Global, order),
func = func
});
}
|
1
|
FilterItem是我自己写的一个类,用来存储设置的Filters跟规则的Func |
1
2
3
4
5
|
public class FilterItem
{
public Filter filter { get ; set ; }
public Func<ControllerContext, ActionDescriptor, bool > func { get ; set ; }
}
|
然后改造GetFilters方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
foreach (FilterItem item in items)
{
if (item.func == null )
{
yield return item.filter;
}
else
{
bool result = item.func(controllerContext, actionDescriptor);
if (result)
yield return item.filter;
}
}
}
|
这样在执行GetFilters的时候就会根据注册的所有Filters进行筛选匹配出符合条件的Filters
最后App_Start中自定义FilterConfig注册Filtes
然后在Global添加自定义的Filters注册
FilterProviders.Providers.Add(Geo.Mvc.Provider.GlobalFilters.Filters);
测试:
新建HomeController,写上两个Action分别为Index和Test
1
2
3
4
5
6
7
8
9
|
public ActionResult Index()
{
return Content( "Index" );
}
public ActionResult Test()
{
return Content( "test" );
}
|
自己随便写个Filter测试
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class TestFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext filterContext)
{
filterContext.HttpContext.Response.Write( "执行前!<br/>" );
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.HttpContext.Response.Write( "执行后!<br/>" );
}
}
|
去App_Start中自定义的Filter注册
1
2
3
4
5
6
7
8
9
10
11
|
public class MyFilterConfig
{
public static void RegistGlobalFilters(Geo.Mvc.Provider.MyFlterCollection filters)
{
//filters.Add(new Filters.TestFilter());
filters.Add((c, a) =>
{
return string .Compare(a.ActionName, "test" , true ) == 0;
}, new Filters.TestFilter());
}
}
|
最后执行Index跟Test方法
结果如下:
这个就是当初我们所希望实现的效果!
不知道怎么放附件,我直接贴我的代码好了!
Global.asax的Application_start中
最后App_Start中的MyFilterConfig.cs
OK!