IFilterProvider i rozdzielenie obaw

Mam sytuację, w której muszę wstrzyknąć pewne zależności w filtrze akcji, a mianowicie moim niestandardowym dostawcą autoryzacji w moim niestandardowym atrybucie autoryzacji. Natknąłem się na wiele osób i postów, którzy mówili, że powinniśmy oddzielać „metadane atrybutów” od „zachowania”. Ma to sens i jest również fakt, że atrybuty filtrów nie są tworzone przez „DependencyResolver”, więc trudno jest wprowadzić zależności.

Zrobiłem więc trochę modyfikacji mojego kodu i chciałem wiedzieć, czy dobrze to zrobiłem (używam Castle Windsor jako struktury DI).

Najpierw pozbawiłem atrybut, aby zawierał tylko surowe dane, których potrzebuję

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

Stworzyłem niestandardowy filtr autoryzacji, który zawierałby logikę określania, czy bieżący użytkownik ma odpowiednie uprawnienia

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

Ostatnią częścią było stworzenie niestandardowego dostawcy filtrów, który pobierałby ten konkretny atrybut i tworzył instancję mojego niestandardowego filtru, przekazując jego zależności i wszelkie potrzebne dane, wyodrębnione z atrybutu.

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

Ostatnim krokiem jest zarejestrowanie dostawcy filtrów w global.asax

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

Zastanawiam się więc najpierw, czy wpadłem na pomysł dobrze i po drugie, co można poprawić.

questionAnswers(2)

yourAnswerToTheQuestion