排除在ASP.NET MVC行动过滤器的方法吗? [英] A way to exclude action filters in ASP.NET MVC?

查看:332
本文介绍了排除在ASP.NET MVC行动过滤器的方法吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我碰到几起案件在ASP.NET MVC,我想申请在每一个操作的操作,除了过滤器一个或两个。例如,假设你有一个的AccountController。在它的每一个动作要求用户先登录,让你在控制器级别添加[授权。但是,假设你想在的AccountController登录页面。问题是,发送到登录页面的用户没有被授权,那么这将导致无限循环。

I've run into several cases in ASP.NET MVC where I wanted to apply an action filter on every action except one or two. For example, say you have an AccountController. Every action in it requires the user be logged in, so you add [Authorize] at the controller level. But say you want to include the login page in AccountController. The problem is, users sent to the login page aren't authorized, so this would result in an infinite loop.

最明显的修复(比移动登录动作到另一个控制器等)是从控制器移动[授权],除了登录所有的操作方法。那么这是不好玩,尤其是当你有很多方法或忘记[授权]添加到新的方法。

The obvious fix (other than moving the Login action to another controller) is to move the [Authorize] from the controller to all action methods except Login. Well that ain't fun, especially when you have a lot of methods or forget to add [Authorize] to a new method.

Rails使这容易排除过滤器的能力。 ASP.NET MVC不会放过你的。所以,我决定把它可能,这比我想象的容易。

Rails makes this easy with an ability to exclude filters. ASP.NET MVC doesn't let you. So I decided to make it possible and it was easier than I thought.

    /// <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();
    }

}

这个例子就在那里。正如你所看到的,这是pretty简单做伟大工程。我希望这是有用的人吗?

The example is right there. As you can see, this was pretty straightforward to do and works great. I hope it's useful to anyone?

推荐答案

我preFER描绘的解决方案<一个href=\"http://blogs.msdn.com/b/rickandy/archive/2011/05/02/securing-your-asp-net-mvc-3-application.aspx\">here.虽然它并不像一般的解决方案是你的,我发现它更简单一点。

I prefer the solution outlined here. Though it's not as generic a solution as yours, I found it a bit more straightforward.

在我的情况,我一直在寻找一种方式来启用一切,但一些项目一个COM pressionFilter。所以,我创建了一个空的属性是这样的:

In my case, I was looking for a way to enable a CompressionFilter on everything but a few items. So I created an empty attribute like this:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class DisableCompression : Attribute { }

然后在主属性,检查是否有像这样的属性presence:

Then in the main attribute, check for the presence of the attribute like so:

[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...
    }
}

虽然我链接到页面提到这是MVC 3,它似乎工作不够好,早在MVC 1为好。

Though the page I linked to mentions that this is for MVC 3, it seems to work well enough way back in MVC 1 as well.

编辑:显示响应的意见在这里一些使用。之前我做了上述改动,它看起来完全一样,只是没有[DisableCom pression]属性标记我想排除方法。有没有涉及其他的重构。

showing some usage here in response to comments. Before I made the changes above, it looked exactly like this, except without the [DisableCompression] attribute flagging the method I wanted to exclude. There's no other refactoring involved.

[CompressionFilter]
public abstract class BaseController : Controller
{
}

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

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

这篇关于排除在ASP.NET MVC行动过滤器的方法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆