ASP.NET MVC5 - 依赖注入和AuthorizeAttribute

18

我为我的问题寻找了很长时间的解决方案。我有一个自定义的AuthorizeAttribute,它需要依赖于一个可以访问DbContext的“Service”。不幸的是,依赖注入在自定义的AuthorizeAttribute中无法工作,并且总是为空。

我想到了一个(对我来说)可接受的解决方案。现在我想知道我的解决方案是否会导致意想不到的行为?

Global.asax.cs

 CustomAuthorizeAttribute.AuthorizeServiceFactory = () => unityContainer.Resolve<AuthorizeService>();

CustomAuthorizeAttribute.cs

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public class CustomAuthorizeAttribute : AuthorizeAttribute
    {
        public static Func<AuthorizeService> AuthorizeServiceFactory { get; set; }

        public Privilege Privilege { get; set; }

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            bool authorized = base.AuthorizeCore(httpContext);
            if (!authorized)
            {
                return false;
            }

            return AuthorizeServiceFactory().AuthorizeCore(httpContext, Privilege);
        }
    }

AuthorizeService.cs

public class AuthorizeService
{
    [Dependency]
    public UserService UserService { private get; set; }

    public bool AuthorizeCore(HttpContextBase httpContext, Privilege privilege)
    {
        ApplicationUser user = UserService.FindByName(httpContext.User.Identity.Name);
        return UserService.UserHasPrivilege(user.Id, privilege.ToString());
    }
}

这是一种可接受的解决方案吗?在将来会遇到什么麻烦,或者也许有更好的方法来使用依赖注入在自定义的AuthorizeAttribute中吗?

3个回答

28
我有一个自定义的AuthorizeAttribute,需要依赖一个可以访问DbContext的"Service"。不幸的是,依赖注入在自定义的AuthorizeAttribute中无效,始终为null。
在System.Web.Mvc命名空间中,IControllerFactory的实现会为Web请求创建你的“Controllers”的实例。控制器工厂使用System.Web.Mvc.DependencyResolver解析每个控制器中的依赖项。
然而,MVC pipeline中的ActionFilters/Attributes并非从Controller Factory中创建,因此不会使用System.Web.Mvc.DependencyResolver来解决依赖项。这就是你的依赖项始终为null的原因。
现在,System.Web.Mvc.DependencyResolver是公开且静态的,所以你可以自己访问它。
    public class AuthorizeService : AuthorizeAttribute
    {
        private UserService UserService
        {
            get
            {
                return DependencyResolver.Current.GetService<UserService>();
            }
        }

        public bool AuthorizeCore(HttpContextBase httpContext, Privilege privilege)
        {
            ApplicationUser user = UserService.FindByName(httpContext.User.Identity.Name);
            return UserService.UserHasPrivilege(user.Id, privilege.ToString());
        }
    }

假设您的UserService具有WebRequest的依赖范围,即其生命周期为每个Web请求一次,并与Web请求的HttpContext的生命周期绑定,则如果已经解析了一个UserService或者这是第一次为给定的Web请求解析UserService,则不会构造一个新的UserService

我同意这个解决方案。 - Sajithd
我遇到了同样的问题,上面的答案很有帮助。但是由于我正在使用Autofac,他们提供了一种通过提供程序解决操作筛选器中依赖关系的方法。 https://autofaccn.readthedocs.io/en/latest/integration/mvc.html?highlight=filter#enable-property-injection-for-action-filters但是当我使用上面的提供程序时遇到的问题是,我的UserService依赖于DbContext,它会抛出一个错误,即 “因为DbContext已被释放,所以无法完成操作” 仅当我在让上一个请求完成之前点击浏览器刷新按钮时才会出现这个错误。 - Ammar Khan

17

在 ASP.NET Core 中,您可以轻松地按以下方式请求服务:

public class CustomAuthAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    public async void OnAuthorization(AuthorizationFilterContext context)
    {
        // var user = context.HttpContext.User;

        // if (!user.Identity.IsAuthenticated)
        // {
        //     context.Result = new UnauthorizedResult();
        //     return;
        // }

        var userService = context.HttpContext.RequestServices.GetService(typeof(UserService)) as UserService;
    }
}

1
问题不在于 ASP.NET Core。其架构上的差异太深了。 - Ernesto
11
这就是为什么我提到了“ASP.NET Core”。我在这里添加了这个答案,因为我猜很多像我一样的人会因为在“ASP.NET Core”中遇到类似的问题而来到这里 :-) - Majid
谢谢Majid。我在想以这种方式获取服务是否存在一些缺点?如果您能解释一下这里发生了什么,我会很感激。虽然结果是我想要的,但我不自信在没有理解的情况下使用它。 - Lukas
@Lukas,这里发生的情况是RequestServices是一个类型为IServiceProvider的属性,它会自动配置并设置为从DI设置中定义和注册的IServiceCollection构建的IServiceProvider。 (对于ASP.NET Core,这通常在Startup.cs文件中完成。) - Kyle L.

5
你也可以尝试这个:

在请求范围内使用ASP.NET Web API和依赖项

public override void OnAuthorization(HttpActionContext filterContext)
{
    var requestScope = filterContext.Request.GetDependencyScope();
    _userService = requestScope.GetService(typeof(IUserService)) as IUserService;
}

//
// Summary:
//     Retrieves the System.Web.Http.Dependencies.IDependencyScope for the given request
//     or null if not available.
//
// Parameters:
//   request:
//     The HTTP request.
//
// Returns:
//     The System.Web.Http.Dependencies.IDependencyScope for the given request or null
//     if not available.
public static IDependencyScope GetDependencyScope(this HttpRequestMessage request);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接