Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/user-interface/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asp.net mvc ASP.NET MVC中排除操作筛选器的方法?_Asp.net Mvc_Action Filter - Fatal编程技术网

Asp.net mvc ASP.NET MVC中排除操作筛选器的方法?

Asp.net mvc ASP.NET MVC中排除操作筛选器的方法?,asp.net-mvc,action-filter,Asp.net Mvc,Action Filter,在ASP.NET MVC中,我遇到过几种情况,其中我希望在每个操作上应用一个操作过滤器,除了一个或两个。例如,假设您有一个AccountController。其中的每个操作都需要用户登录,因此您可以在控制器级别添加[Authorize]。但假设您想在AccountController中包含登录页面。问题是,发送到登录页面的用户未经授权,因此这将导致无限循环 显而易见的修复方法(除了将登录操作移动到另一个控制器)是将[Authorize]从控制器移动到除登录之外的所有操作方法。这一点都不好玩,尤其

在ASP.NET MVC中,我遇到过几种情况,其中我希望在每个操作上应用一个操作过滤器,除了一个或两个。例如,假设您有一个AccountController。其中的每个操作都需要用户登录,因此您可以在控制器级别添加[Authorize]。但假设您想在AccountController中包含登录页面。问题是,发送到登录页面的用户未经授权,因此这将导致无限循环

显而易见的修复方法(除了将登录操作移动到另一个控制器)是将[Authorize]从控制器移动到除登录之外的所有操作方法。这一点都不好玩,尤其是当你有很多方法或者忘记给一个新方法添加[Authorize]的时候

Rails通过排除过滤器的功能使这变得容易。ASP.NET MVC不允许您这样做。所以我决定让它成为可能,这比我想象的要容易

    /// <summary>
/// This will disable any filters of the given type from being applied.  This is useful when, say, all but on action need the Authorize filter.
/// </summary>
[AttributeUsage(AttributeTargets.Method|AttributeTargets.Class, AllowMultiple=true)]
public class ExcludeFilterAttribute : ActionFilterAttribute
{

    public ExcludeFilterAttribute(Type toExclude)
    {
        FilterToExclude = toExclude;
    }

    /// <summary>
    /// The type of filter that will be ignored.
    /// </summary>
    public Type FilterToExclude
    {
        get;
        private set;
    }
}

/// <summary>
/// A subclass of ControllerActionInvoker that implements the functionality of IgnoreFilterAttribute.  To use this, just override Controller.CreateActionInvoker() and return an instance of this.
/// </summary>
public class ControllerActionInvokerWithExcludeFilter : ControllerActionInvoker
{
    protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
        //base implementation does all the hard work.  we just prune off the filters to ignore
        var filterInfo = base.GetFilters(controllerContext, actionDescriptor);           
        foreach( var toExclude in filterInfo.ActionFilters.OfType<ExcludeFilterAttribute>().Select(f=>f.FilterToExclude).ToArray() )
        {
            RemoveWhere(filterInfo.ActionFilters, filter => toExclude.IsAssignableFrom(filter.GetType()));
            RemoveWhere(filterInfo.AuthorizationFilters, filter => toExclude.IsAssignableFrom(filter.GetType()));
            RemoveWhere(filterInfo.ExceptionFilters, filter => toExclude.IsAssignableFrom(filter.GetType()));
            RemoveWhere(filterInfo.ResultFilters, filter => toExclude.IsAssignableFrom(filter.GetType()));
        }
        return filterInfo;
    }


    /// <summary>
    /// Removes all elements from the list that satisfy the condition.  Returns the list that was passed in (minus removed elements) for chaining.  Ripped from one of my helper libraries (where it was a pretty extension method).
    /// </summary>
    private static IList<T> RemoveWhere<T>(IList<T> list, Predicate<T> predicate)
    {

        if (list == null || list.Count == 0)
            return list;
        //note: didn't use foreach because an exception will be thrown when you remove items during enumeration
        for (var i = 0; i < list.Count; i++)
        {
            var item = list[i];
            if (predicate(item))
            {
                list.RemoveAt(i);
                i--;
            }
        }
        return list;
    }
}

/// <summary>
/// An example of using the ExcludeFilterAttribute.  In this case, Action1 and Action3 require authorization but not Action2.  Notice the CreateActionInvoker() override.  That's necessary for the attribute to work and is probably best to put in some base class.
/// </summary>
[Authorize]
public class ExampleController : Controller
{
    protected override IActionInvoker CreateActionInvoker()
    {
        return new ControllerActionInvokerWithExcludeFilter();
    }

    public ActionResult Action1()
    {
        return View();
    }

    [ExcludeFilter(typeof(AuthorizeAttribute))]
    public ActionResult Action2()
    {
        return View();
    }

    public ActionResult Action3()
    {
        return View();
    }

}
//
///这将禁止应用给定类型的任何筛选器。例如,当除操作外的所有操作都需要授权筛选器时,这非常有用。
/// 
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,AllowMultiple=true)]
公共类ExcludeFilterAttribute:ActionFilterAttribute
{
public ExcludeFilterAttribute(类型toExclude)
{
FilterToExclude=toExclude;
}
/// 
///将被忽略的筛选器类型。
/// 
公共类型筛选器排除
{
得到;
私人设置;
}
}
/// 
///ControllerActionInvoker的一个子类,实现IgnoreFilterAttribute的功能。要使用它,只需重写Controller.CreateActionInvoker()并返回此函数的一个实例。
/// 
公共类ControllerActionInvokerWithExcludeFilter:ControllerActionInvoker
{
受保护的覆盖筛选器信息GetFilters(ControllerContext ControllerContext,ActionDescriptor ActionDescriptor)
{
//基本实现完成了所有艰苦的工作。我们只是删掉了过滤器,忽略了它们
var filterInfo=base.GetFilters(controllerContext,actionDescriptor);
foreach(在filterInfo.ActionFilters.OfType()中使用var toExclude.Select(f=>f.FilterToExclude.ToArray())
{
RemoveWhere(filterInfo.ActionFilters,filter=>toExclude.IsAssignableFrom(filter.GetType());
RemoveWhere(filterInfo.AuthorizationFilters,filter=>toExclude.IsAssignableFrom(filter.GetType());
RemoveWhere(filterInfo.ExceptionFilters,filter=>toExclude.IsAssignableFrom(filter.GetType());
RemoveWhere(filterInfo.ResultFilters,filter=>toExclude.IsAssignableFrom(filter.GetType());
}
返回过滤器信息;
}
/// 
///从列表中删除满足条件的所有元素。返回为链接而传入的列表(减去已删除的元素)。从my helper库(其中是一个漂亮的扩展方法)中删除。
/// 
私有静态IList RemoveWhere(IList列表,谓词)
{
if(list==null | | list.Count==0)
退货清单;
//注意:未使用foreach,因为在枚举期间删除项时将引发异常
对于(var i=0;i

例子就在这里。正如您所看到的,这是非常简单的,而且效果非常好。我希望它对任何人都有用?

我更喜欢概述的解决方案。虽然它不像您的解决方案那样通用,但我发现它更简单一些

在我的例子中,我正在寻找一种方法来启用压缩过滤器,除了一些项目之外。所以我创建了一个空属性,如下所示:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class DisableCompression : Attribute { }
然后在主属性中,检查属性是否存在,如下所示:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class CompressionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        bool disabled = filterContext.ActionDescriptor.IsDefined(typeof(DisableCompression), true) ||
                        filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(DisableCompression), true);
        if (disabled)
            return;

        // action filter logic here...
    }
}
虽然我链接到的页面提到这是针对MVC3的,但它在MVC1中似乎也运行得很好

编辑:在这里显示一些用法以回应评论。在我进行上述更改之前,它看起来与此完全相同,只是没有标记我要排除的方法的[DisableCompression]属性。没有涉及其他重构

[CompressionFilter]
public abstract class BaseController : Controller
{
}

public class SomeController : BaseController
{
    public ActionResult WantThisActionCompressed()
    {
        // code
    }

    [DisableCompression]
    public ActionResult DontWantThisActionCompressed()
    {
        // code
    }
}

多年前,我假设ASP.NET MVC中没有添加
[AllowAnnonymous]
属性。今天,我可以将控制器上的
[Authorize]
属性应用于所有操作方法,我只需在需要未经授权用户的操作中通过将
[AllowAnonymous]
属性添加到特定操作中来覆盖此属性

List.RemoveAll
存在:是的,我知道List.RemoveAll。问题是System.Web.Mvc.FilterInfo将这些集合公开为IList而不是List,即使底层实现是List。我本可以铸造列表并使用RemoveAll,但我觉得最好尊重API。我的小助手方法有点难看,是的。我通常将其作为扩展方法塞进助手库中,这使代码更加清晰。但为此,我希望它通过复制粘贴进行编译。您认为呢?排除现有筛选器的另一种方法是impl