加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

asp.net-mvc – 一种在ASP.NET MVC中排除操作过滤器的方法?

发布时间:2020-12-15 18:41:29 所属栏目:asp.Net 来源:网络整理
导读:我在ASP.NET MVC中遇到过几种情况,其中我想对除了一个或两个以外的每个动作应用一个动作过滤器。例如,说你有一个AccountController。其中的每个操作都要求用户登录,因此您可以在控制器级别添加[授权]。但是说你想在AccountController中包含登录页面。问题
我在ASP.NET MVC中遇到过几种情况,其中我想对除了一个或两个以外的每个动作应用一个动作过滤器。例如,说你有一个AccountController。其中的每个操作都要求用户登录,因此您可以在控制器级别添加[授权]。但是说你想在AccountController中包含登录页面。问题是,发送到登录页面的用户没有被授权,所以这将导致无限循环。

明显的修复(除了将登录操作移动到另一个控制器之外)是将[授权]从控制器移动到除登录之外的所有操作方法。那么这不是乐趣,特别是当你有很多方法或忘记添加[授权]一个新的方法。

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

}

例子就在那里。你可以看到,这是非常简单的做,并且很好。我希望对任何人都有用吗?

解决方法

我更喜欢 here所述的解决方案。虽然它并不像您的一般解决方案,我发现它更直接。

在我的情况下,我正在寻找一种方法来启用一个压缩过滤器,除了几个项目。所以我创建了一个这样的空属性:

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

虽然我链接到的页面提到这是MVC 3,但似乎在MVC 1中也可以运行得很好。

编辑:在回应评论时显示一些用法。在我进行上述更改之前,它看起来完全如此,除非没有[DisableCompression]属性标记我想排除的方法。没有其他重构涉及。

[CompressionFilter]
public abstract class BaseController : Controller
{
}

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

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读