最近在公司项目中用MVC内置的权限过滤器实现权限控制功能,查阅以前的代码发现权限过滤接口已经被实现在其他地方,作用是用户访问系统任何页面时都要判断是否登录,如果没有登录,就会自动读取本机的域账号信息,并自动完成登录动作。现在遇到的问题是,如果新加的权限验证功能基于权限过滤器,就必须考虑过滤器的执行先后顺序,由于权限验证基于用户信息,所以我要保证用户自动登录的过滤器在权限过滤器之前被执行,但是MVC的权限过滤器默认是优先级最高的,我又不想让非权限验证的功能丢在权限过滤器中执行,又想要这个过滤器执行优先级一定高于权限过滤器,怎么办呢?看来只有通过扩展MVC内置过滤器的方式实现了。
一、初步计划
自定义一个过滤器接口IFirstFilter,它会在MVC框架中被最先调用,程序员只需要创建实现该接口的特性,就可以实现自定义过滤器。
二、实现步骤
1) 重写ControllerActionInvoker的方法InvokeAction
由于MVC框架的action方法调用统一通过InvokeAction执行,在这个方法中会调用MVC内置过滤器,我们通过重写它来给自定义过滤器“内置”进去。另外还自定义两个方法,GetFirstFilters方法用来通过反射特性获取自定义的特性,InvokeFirstFilters用来执行自定义的过滤器。
新建类文件UserControllerActionInvoker.cs,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Reflection;
namespace MvcUserFilter.MVC
{
public class UserControllerActionInvoker : ControllerActionInvoker
{
public override bool InvokeAction(ControllerContext controllerContext, string actionName)
{
if (controllerContext != null && !string.IsNullOrEmpty(actionName))
{
ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
if (actionDescriptor != null)
{
IList<IFirstFilter> firstFilters = GetFirstFilters(actionDescriptor);
FirstFilterContext firstContext = InvokeFirstFilters(controllerContext, firstFilters, actionDescriptor);
if (firstContext.Result != null)
{
InvokeActionResult(controllerContext, firstContext.Result);
return true;
}
}
}
return base.InvokeAction(controllerContext, actionName);
}
private IList<IFirstFilter> GetFirstFilters(ActionDescriptor actionDescriptor)
{
MethodInfo methodInfo = (actionDescriptor as ReflectedActionDescriptor).MethodInfo;
FilterAttribute[] typeFilters = (FilterAttribute[])methodInfo.ReflectedType.GetCustomAttributes(typeof(FilterAttribute), true /* inherit */);
FilterAttribute[] methodFilters = (FilterAttribute[])methodInfo.GetCustomAttributes(typeof(FilterAttribute), true /* inherit */);
List<FilterAttribute> orderedFilters = typeFilters.Concat(methodFilters).OrderBy(attr => attr.Order).ToList();
IList<IFirstFilter> firstFilters = new List<IFirstFilter>();
foreach (FilterAttribute filter in orderedFilters)
{
IFirstFilter castFilter = filter as IFirstFilter;
if (castFilter != null)
{
firstFilters.Add(castFilter);
}
}
return firstFilters;
}
private FirstFilterContext InvokeFirstFilters(ControllerContext controllerContext, IList<IFirstFilter> firstFilters, ActionDescriptor actionDescriptor)
{
FirstFilterContext context = new FirstFilterContext(controllerContext, actionDescriptor);
foreach (IFirstFilter filter in firstFilters)
{
filter.OnFirstFilterDoing(context);
if (context.Result != null)
{
break;
}
}
return context;
}
}
}
2) 定义自定义的过滤器接口
新建类文件IFirstFilter.cs,代码如下:
namespace MvcUserFilter.MVC
{
interface IFirstFilter
{
void OnFirstFilterDoing(FirstFilterContext filterContext);
}
}
3) 定义自定义的过滤器上下文
新建类文件FirstFilterContext.cs,代码如下:
namespace MvcUserFilter.MVC
{
public class FirstFilterContext : ControllerContext
{
public FirstFilterContext()
{
}
[Obsolete("The recommended alternative is the constructor AuthorizationContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor).")]
public FirstFilterContext(ControllerContext controllerContext)
: base(controllerContext)
{
}
[SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
Justification = "The virtual property setters are only to support mocking frameworks, in which case this constructor shouldn't be called anyway.")]
public FirstFilterContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
: base(controllerContext)
{
if (actionDescriptor == null)
{
throw new ArgumentNullException("actionDescriptor");
}
ActionDescriptor = actionDescriptor;
}
public virtual ActionDescriptor ActionDescriptor
{
get;
set;
}
public ActionResult Result
{
get;
set;
}
}
}
4) 重写控制器CreateActionInvoker方法,创建自定义ActionInvoker实例
定义一个UserController.cs,继承自Controller,重写方法CreateActionInvoker方法,代码如下:
namespace MvcUserFilter.MVC
{
public class UserController : Controller
{
protected override IActionInvoker CreateActionInvoker()
{
return new UserControllerActionInvoker();
}
}
}
三、使用方法
到此我们已经成功的将自定义的过滤器“内置”到MVC框架里了,现在看看怎么使用,使用方式跟MVC自带的过滤器基本一致(目前只能通过特性体现,不支持控制器虚方法派生):
首先,定义一个FirstFilterAttribute特性,从FilterAttribute, IFirstFilter 派生,实现接口IFirstFilter的方法OnFirstFilterDoing
public class FirstFilterAttribute : FilterAttribute, IFirstFilter
{
public void OnFirstFilterDoing(FirstFilterContext filterContext)
{
filterContext.Result = new ContentResult()
{
Content="用户自定义过滤器被执行了。",
};
}
}
然后,就可以在控制器的action上使用了,注意,控制器要从UserController继承
public class HomeController : UserController
{
[FirstFilter]
public ActionResult Index()
{
ViewData["Message"] = "欢迎使用 ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
}
四、执行结果
编译运行,访问首页时页面输出“用户自定义过滤器被执行了。”
出处: [Lipan] ( http://www.cnblogs.com/lipan/)
版权声明:本文的版权归作者与博客园共有。转载时须注明本文指向型链接,否则作者将保留追究其法律责任。