.NET Core WebAPI依赖注入解析为null

时间:2018-02-02 16:08:51

标签: c# authentication asp.net-web-api .net-core

我使用.NET Core WebAPI和依赖注入以及多个身份验证模式(http basic,access keys,JWT)。我注入一些需要一些经过身份验证的用户数据的业务服务。如果用户通过任何auth中间件进行身份验证,则DI工作正常。如果用户未通过身份验证,则DI无法解析某些服务。我需要DI才能返回null

怎么可能?以下代码将导致异常,因此不允许null

services.AddTransient<IMasterRepository>(serviceProvider =>
        {
            var _serviceFactory = new RepositoriesFactory(Configuration);

            if (!Authenticated)
            {
                return null;
            }

            return _serviceFactory.CreateMasterRepository();
        });

另外,我无法在auth中间件中返回401,因为另一个中间件可能成功(探索:无法在http基础auth中间件中返回401因为下一个,JWT,可能会成功)

另外,我无法添加&#34;所需的身份验证&#34;检查所有auth中间件后,因为某些控制器是公共的(不需要身份验证/依赖注入)。

有什么建议吗? 谢谢!

4 个答案:

答案 0 :(得分:3)

在我看来,这听起来像是您的依赖项设置存在问题。

如果身份验证成功,则所有身份验证中间件都应在ClaimsPrincipal方法中在HttpContext上设置Invoke

尽管服务可能需要能够访问ClaimsPrincipal才能正常运行,但是您可以通过在构造函数中注入IHttpContextAccessor并在ConfigureServices方法中启用它来实现此目的在Startup.cs中。

获取DI容器以返回null只是意味着您必须在整个代码中进行很多null检查。确保正确设置ClaimsPrincipal意味着您可以利用[Authorize]属性来控制对特定控制器或方法或设置Policy-based authorization的访问,它们应在所有身份验证中间件之后返回正确的状态代码已经运行。

答案 1 :(得分:1)

重构代码,以便知道返回类型。

services.AddTransient<IMasterRepository>(serviceProvider => {
    IMasterRepository result = null;
    var _serviceFactory = new RepositoriesFactory(Configuration);
    if (Authenticated) {
        result = _serviceFactory.CreateMasterRepository();
    }
    return result;
});

或者考虑创建一个从界面派生的NullObject

public class NullRepository : IMasterRepository {
    //...implement members that do nothing and may return empty collections.
}

在调用时不执行任何操作。

services.AddTransient<IMasterRepository>(serviceProvider => {
    IMasterRepository result = NullRepository();
    var _serviceFactory = new RepositoriesFactory(Configuration);
    if (Authenticated) {
        result = _serviceFactory.CreateMasterRepository();
    }
    return result;
});

答案 2 :(得分:1)

虽然您可以创建一个能够容纳服务实例或null的包装器类-或者-您可以在用户未通过身份验证的情况下注入虚拟服务,但从干净代码的角度来看,不建议这样做。两种解决方案都像是第一种代码味道:您必须在所有地方放置空检查,即使在您希望获得服务的地方也是如此。后者:似乎代码可以实际使用该服务,但尚不清楚是否将提供虚拟服务。

为了保持代码的清洁,我只将不需要这些服务的公用路由移到了一个不依赖该服务的单独的控制器类(具有与现在相同的路由)。这样,您可以按原样注册存储库并避免魔术。

答案 3 :(得分:1)

注册为s没有问题。只会在解决中出现问题。

换句话说,如果您注册:

null

然后尝试:

services.AddTransient<IMasterRepository>(provider => null);

您将在运行时收到private readonly IMasterRepository _repository; public SomeController(IMasterRepository repository) { _repository = repository; } ,并显示类似以下消息:

尝试激活'MyApp.Controllers.SomeController'时无法解析'MyApp.IMasterRepository'类型的服务

但是,有一个简单的解决方法。而不是注入接口,而是注入该接口的InvalidOperationException

IEnumerable

您可能会认为应该是private readonly IMasterRepository _repository; public SomeController(IEnumerable<IMasterRepository> repositories) { _repository = repositories.First(); // (using System.Linq) } ,但是确实会有一个包含您注册的FirstOrDefault的项目。

该方法之所以有效,是因为ASP.Net Core中的DI支持注册给定类型的多个实现,并且在注册时不区分null和对象实例。

请记住,即使此方法可行,也不建议这样做,因为现在null变量可能为空,并且每次访问它时都必须使用空检查。例如:_repositoryif (_repository != null) { _repository.DoSomething(); }。大多数人不希望编写这样的代码。

这涵盖了问题的DI部分。但是,如果确实确实是问题出在auth上,那么ste-fu's answer描述了一种更合适的方法。