65.9K
CodeProject 正在变化。 阅读更多。
Home

Fluent Filters - ASP.NET MVC 2 的全局动作过滤器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (4投票s)

2010年11月5日

Apache

3分钟阅读

viewsIcon

33803

downloadIcon

417

这篇文章描述了一个用于 ASP.NET MVC 的小型库,可以用作注册全局过滤器的工具。它类似于 ASP.NET MVC 3 中的全局过滤器,但具有不同的实现和功能。

Fluent Filters

引言

曾经,在一个用 ASP.NET MVC 2 编写的项目中,我需要实现一种方法来为应用程序中的所有控制器注册动作过滤器,因为如果你想全局执行相同的操作,为每个控制器类指定属性不是很方便,而且缺乏灵活性。后来,我只在 ASP.NET MVC 3 中发现了对此的提及。

在那之后,我决定自己编写类似的功能,为自己设定了一系列必须执行的要求

  1. 在应用程序中轻松注册过滤器
  2. 能够向过滤器添加条件
  3. 能够对过滤器使用依赖注入

我有一个有趣的方法,我决定将所有这些结合到一个单独的库中,以便与他人分享。

你可以在 CodePlex 上的 项目页面 找到最新版本的二进制文件和源代码。工作应用程序示例位于源代码的 src\Samples 文件夹中。

Using the Code

FluentFilters 帮助你在你的 ASP.NET MVC 应用程序中实现全局过滤器功能。全局过滤器是一个将对每个控制器的每个动作运行的过滤器。创建一个自定义过滤器供全局使用非常简单。你所需要做的就是用一个或多个接口实现你的自定义过滤器类,这些接口是四种类型的 ASP.NET MVC 过滤器

  • IAuthorizationFilter
  • IActionFilter
  • IResultFilter
  • IExceptionFilter

例如,一个过滤器可能看起来像这样

public class CustomFilter: IResultFilter, IAuthorizationFilter
{
    #region IResultFilter Members

    public void OnResultExecuted(ResultExecutedContext filterContext)
    {
        // Some logic here ...
    }

    public void OnResultExecuting(ResultExecutingContext filterContext) 
    {
        // Or here ...
    }

    #endregion

    #region IAuthorizationFilter Members

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        // May be here?
    }

    #endregion
}

为了使用过滤器,你应该使用类 FluentFiltersBuilder。如果你在没有控制反转(IoC) 容器的情况下构建 MVC 应用程序,这是推荐的使用方法。 使用 IoC 容器的 FluentFilters 比使用 FluentFiltersBuilder 更好。在这种情况下,你可以管理过滤器对象的创建和生命周期,并使用依赖注入。要实现这一点,你需要执行几个步骤。

  1. 实现继承 FilterRegistry 类的自定义类,并重写 GetFilterInstance 方法以按类型解析对象,而不是创建一个新的对象。这个类将代替 FluentFiltersBuilder 用于注册过滤器。
  2. 创建一个自定义控制器工厂以支持 IoC 容器并注册它。

你可以在源代码的 src\Samples\FluentFilters.Samples.IoC 文件夹中找到带有 IoC 容器的示例应用程序。

对于基本配置,编辑 Global.asax.cs 文件就足够了,如下所示

// Global.asax.cs

using System.Web.Mvc;
using System.Web.Routing;
using FluentFilters;
using FluentFilters.Criteria;
using Website.Core.Filters;
 
namespace Website
{
    public class MvcApplication : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", 
		id = UrlParameter.Optional } // Parameter defaults
            );
        }
 
        private static void RegisterFluentFilters()
        {
            // Register global filter
            FluentFiltersBuilder.Current.Add();
        }
 
        private static void RegisterControllerFactory()
        {
            // Set controller factory
            ControllerBuilder.Current.SetControllerFactory
			(new FluentFiltersControllerFactory());
        }
 
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
 
            RegisterRoutes(RouteTable.Routes);
 
            RegisterFluentFilters();
 
            RegisterControllerFactory();
        }
    }
}

你可以在方法 RegisterFluentFilters 中看到已注册的过滤器 BrowserDetectionFilter

稍后,当你添加全局过滤器供使用时,你可以为每个过滤器设置链式条件。通过条件,你可以设置是否执行过滤器。创建条件也非常简单。 你需要从 IFilterCriteria 接口继承你的自定义条件类,并且只实现方法 Match。该库已经提供了三个条件供使用

  • ActionFilterCriteria - 按指定的动作过滤
  • AreaFilterCriteria - 按指定区域过滤
  • ControllerFilterCriteria - 按指定的控制器过滤

例如,下面的代码片段显示了条件 ActionFilterCriteria 的源代码

public class ActionFilterCriteria : IFilterCriteria
{
    private readonly string _actionName;
 
    /// <summary>

    /// Filter by specified action
    /// </summary>
    /// <param name="actionName">Name of the action</param>

    public ActionFilterCriteria(string actionName)
    {
        _actionName = actionName;
    }
 
    #region Implementation of IActionFilterCriteria
 
    public bool Match(FilterRegistryContext context)
    {
        return string.Equals(_actionName, 
	context.ControllerContext.RouteData.GetRequiredString("action"), 
	StringComparison.OrdinalIgnoreCase);
    }
 
    #endregion

}

对于每个过滤器,你可以添加两个链式条件。 这是必需的条件,也应该被排除。如果未指定条件,则将在整个应用程序中执行过滤器。

在下面的代码中,我们为区域 "Blog" 添加了过滤器 BrowserDetectionFilter ,并为控制器 "Account" 排除。

FluentFiltersBuilder.Current.Add
(c =>

{
    c.Require(new AreaFilterCriteria("Blog")); // Execute if current area "Blog"
    c.Exclude(new ControllerFilterCriteria("Account")); // Ignore if current 
						// controller "Account"

});

你可以通过方法 And(IFilterCriteria criteria)Or(IFilterCriteria criteria) 构建条件链。方法的工作方式类似于条件逻辑运算符 &&||

简化示例

registry.Add<displaytopbannerfilter>(c =>
{
    c.Require(new IsFreeAccountFilterCriteria()).Or(new AreaFilterCriteria("Blog")).
	Or(new AreaFilterCriteria("Forum")).And(new IsMemberFilterCriteria());
    c.Exclude(new AreaFilterCriteria("Administrator")).
	Or(new ControllerFilterCriteria("Account")).
	And(new ActionFilterCriteria("LogOn"));
});

如果用 C# 语言来说,那么上面的代码可以理解为(类似于伪代码)

if( IsFreeAccountFilterCriteria() || area == "Blog" || 
	(area == "Forum" && IsMemberFilterCriteria()) ) 
{
    if(area != "Administrator")
    {
        DisplayTopBannerFilter();
    }
    else if(controller != "Account" && action != "LogOn")
    {
        DisplayTopBannerFilter();
    }
}

历史

  • 2010 年 11 月 4 日:初始版本
  • 2010 年 11 月 5 日:更新文章
© . All rights reserved.