IFilterProvider和关注点分离 [英] IFilterProvider and separation of concerns

查看:213
本文介绍了IFilterProvider和关注点分离的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个情况我需要在我的自定义授权属性注入一些依赖于动作过滤器,即我的自定义授权提供。我偶然发现了很多人的职位和谁是说,我们应该从'行为'分离'属性的元数据。这是有意义的,并也有一个事实,即过滤器属性不通过DependencyResolver实例化,因此难以注入的依赖关系。

I have a situation where I need to inject some dependencies in a action filter, namely, my custom authorization provider in my custom authorization attribute. I stumbled upon a lot of people and posts who were saying that we should be separating the 'attribute metadata' from the 'behavior'. This makes sense and there is also the fact that filter attributes are not instantiated through the 'DependencyResolver' so it is difficult to inject the dependencies.

所以,我做我的code稍微重构和我想知道如果我是正确的(我使用温莎城堡为DI框架)。

So I did a little refactoring of my code and I wanted to know if I had it right (I'm using Castle Windsor as the DI framework).

首先,我剥我的属性只包含原始数据,我需要

First off I stripped my attribute to contain only the raw data I need

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MyAuthorizeAttribute : Attribute
{
    public string Code { get; set; }
}

我创建了一个自定义授权过滤器将包含确定当前用户具​​有适当授权的逻辑

I created a custom authorization filter that would contain the logic of determining if the current user has the proper authorization

public class MyAuthorizationFilter : IAuthorizationFilter
{
    private IAuthorizationProvider _authorizationProvider;
    private string _code;

    public MyAuthorizationFilter(IAuthorizationProvider authorizationProvider, string code)
    {
        Contract.Requires(authorizationProvider != null);
        Contract.Requires(!string.IsNullOrWhiteSpace(code));

        _authorizationProvider = authorizationProvider;
        _code = code;
    }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }

        if (filterContext.HttpContext.Request.IsAuthenticated)
        {
            BaseController controller = filterContext.Controller as BaseController;
            if (controller != null)
            {
                if (!IsAuthorized(controller.CurrentUser, controller.GetCurrentSecurityContext()))
                {
                    // forbidden
                    filterContext.RequestContext.HttpContext.Response.StatusCode = 403;
                    if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
                    {
                        filterContext.Result = new RedirectToRouteResult("default", new RouteValueDictionary(new
                        {
                            action = "http403",
                            controller = "error"
                        }), false);
                    }
                    else
                    {
                        filterContext.Result = controller.InvokeHttp404(filterContext.HttpContext);
                    }
                }
            }
            else
            {

            }
        }
        else
        {
            filterContext.Result = new RedirectResult(FormsAuthentication.LoginUrl);
        }
    }

    private bool IsAuthorized(MyUser user, BaseSecurityContext securityContext)
    {
        bool has = false;
        if (_authorizationProvider != null && !string.IsNullOrWhiteSpace(_code))
        {
            if (user != null)
            {
                if (securityContext != null)
                {
                    has = _authorizationProvider.HasPermission(user, _code, securityContext);
                }
            }
        }
        else
        {
            has = true;
        }
        return has;
    }
}

最后一部分是创建一个自定义过滤器供应商,会取这个特定的属性和实例我路过它的依赖,它需要的任何数据,从属性提取自定义过滤器。

The last part was to create a custom filter provider that would fetch this specific attribute and instantiate my custom filter passing its dependencies and any data it needs, extracted from the attribute.

public class MyAuthorizationFilterProvider : IFilterProvider
{
    private IWindsorContainer _container;

    public MyAuthorizationFilterProvider(IWindsorContainer container)
    {
        Contract.Requires(container != null);
        _container = container;
    }

    public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
        Type controllerType = controllerContext.Controller.GetType();
        var authorizationProvider = _container.Resolve<IAuthorizationProvider>();
        foreach (MyAuthorizeAttribute attribute in controllerType.GetCustomAttributes(typeof(MyAuthorizeAttribute), false))
        {
            yield return new Filter(new MyAuthorizationFilter(authorizationProvider, attribute.Code), FilterScope.Controller, 0);
        }
        foreach (MyAuthorizeAttribute attribute in actionDescriptor.GetCustomAttributes(typeof(MyAuthorizeAttribute), false))
        {
            yield return new Filter(new MyAuthorizationFilter(authorizationProvider, attribute.Code), FilterScope.Action, 0);
        }
    }
}

最后一步是在Global.asax中注册该过滤器提供商

The last step is the register the filter provider in the global.asax

FilterProviders.Providers.Add(new MyAuthorizationFilterProvider(_container));

所以我想知道第一,如果我得到了正确的理念和第二,有什么可以改进。

So I'm wondering first, if I got the idea right and second, what could be improved.

推荐答案

是的,我觉得你有这个想法的权利。我喜欢你分离属性和过滤器实现之间的关切,我喜欢你正在使用的构造DI而非财产DI

Yes, I think you got the idea right. I like that you're separating concerns between the attribute and the filter implementation, and I like that you're using constructor DI rather than property DI.

您的方法效果很好,如果你只有一个类型的过滤器。我认为最大的潜在领域的提升空间,如果你有多个类型的过滤器,将过滤器供应商是如何实现的。目前,该过滤器提供紧密耦合到属性和过滤器情况下,它被提供

Your approach works well if you only have one type of filter. I think the biggest potential area for improvement, if you had more than one type of filter, would be how the filter provider is implemented. Currently, the filter provider is tightly coupled to the attribute and filter instances it is providing.

如果你愿意的属性与过滤器相结合,并使用属性DI,有一个简单的办法有一个更解耦滤波器供应商。以下是方法的两个例子:
<一href=\"http://www.thecodinghumanist.com/blog/archives/2011/1/27/structuremap-action-filters-and-dependency-injection-in-asp-net-mvc-3\" rel=\"nofollow\">http://www.thecodinghumanist.com/blog/archives/2011/1/27/structuremap-action-filters-and-dependency-injection-in-asp-net-mvc-3
http://lozanotek.com/blog/archive/2010/10/12/dependency_injection_for_filters_in_mvc3.aspx

If you're willing to combine the attribute with the filter and use property DI, there's a simple way to have a more decoupled filter provider. Here are two examples of that approach: http://www.thecodinghumanist.com/blog/archives/2011/1/27/structuremap-action-filters-and-dependency-injection-in-asp-net-mvc-3 http://lozanotek.com/blog/archive/2010/10/12/dependency_injection_for_filters_in_mvc3.aspx

有与当前的办法来解决两方面的挑战:
1.注射一些,但并非所有通过的DI滤波器构造参数。
2.从一个(依赖注入)的过滤器实例属性映射。

There are two challenges to solve with the current approach: 1. Injecting some, but not all, of the filter constructor parameters via DI. 2. Mapping from an attribute to a (dependency-injected) filter instance.

目前,你人工手动操作的两个,这当然是很好,当只有一种过滤器/属性。如果有更多,你可能想为两个部分一个更通用的方法。

Currently, you're doing both manually, which is certainly fine when there's only one filter/attribute. If there were more, you'd probably want a more general approach for both parts.

有关挑战#1,你可以使用像一个_container.Resolve过载,使您在传递参数。该解决方案是相当容器特定的,可能是有点棘手。

For challenge #1, you could use something like a _container.Resolve overload that lets you pass in arguments. That solution is rather container-specific and probably a bit tricky.

另一种解决方案,我将在这里描述,分离出一个工厂类,只需要在其构造函数依赖,产生既需要DI和非DI参数的过滤器实例。

Another solution, which I'll describe here, separates out a factory class that only takes dependencies in its constructor and produces a filter instance requiring both DI and non-DI arguments.

下面是该工厂可能看起​​来是这样的:

Here's what that factory might look like:

public interface IFilterInstanceFactory
{
    object Create(Attribute attribute);
}

您会然后实现每个属性/滤波器对工厂:

You'd then implement a factory for each attribute/filter pair:

public class MyAuthorizationFilterFactory : IFilterInstanceFactory
{
    private readonly IAuthorizationProvider provider;

    public MyAuthorizationFilterFactory(IAuthorizationProvider provider)
    {
        this.provider = provider;
    }

    public object Create(Attribute attribute)
    {
        MyAuthorizeAttribute authorizeAttribute = attribute as MyAuthorizeAttribute;

        if (authorizeAttribute == null)
        {
            return null;
        }

        return new MyAuthorizationFilter(provider, authorizeAttribute.Code);
   }
}

您可以通过只注册IFilterInstanceFactory的每个实现与CastleWindsor解决难题#2。

You can solve challenge #2 by just registering each implementation of IFilterInstanceFactory with CastleWindsor.

该滤波器提供者现在可以从特定属性和过滤器的任何知识去耦:

The filter provider can now be decoupled from any knowledge of specific attributes and filters:

public class MyFilterProvider : IFilterProvider
{
    private IWindsorContainer _container;

    public MyFilterProvider(IWindsorContainer container)
    {
        Contract.Requires(container != null);
        _container = container;
    }

    public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
        Type controllerType = controllerContext.Controller.GetType();
        var authorizationProvider = _container.Resolve<IAuthorizationProvider>();
        foreach (FilterAttribute attribute in controllerType.GetCustomAttributes(typeof(FilterAttribute), false))
        {
            object instance = Resolve(attribute);
            yield return new Filter(instance, FilterScope.Controller, 0);
        }
        foreach (FilterAttribute attribute in actionDescriptor.GetCustomAttributes(typeof(FilterAttribute), false))
        {
            object instance = Resolve(attribute);
            yield return new Filter(instance, FilterScope.Action, 0);
        }
    }

    private object Resolve(Attribute attribute)
    {
        IFilterInstanceFactory[] factories = _container.ResolveAll<IFilterInstanceFactory>();

        foreach (IFilterInstanceFactory factory in factories)
        {
            object dependencyInjectedInstance = factory.Create(attribute);

            if (dependencyInjectedInstance != null)
            {
                return dependencyInjectedInstance;
            }
        }

        return attribute;
    }
}

大卫

这篇关于IFilterProvider和关注点分离的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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