.Net Core-从API中间件向存储库层注入依赖项IUserInfo

时间:2018-07-02 21:14:59

标签: c# asp.net-core dependency-injection repository-pattern asp.net-core-webapi

假设我从头到尾遵循以下结构化项目层,如存储库->服务-> API ,代码示例:

存储库:

public interface IUserInfo
{
    int UID{ get; set; }
}
public class UserInfo : IUserInfo
{
    public int UID { get; set; }
}
public class ProductionRepository : Repository, IProductionRepository {
    public ProductionRepository(IUserInfo userInfo, StoreDbContext dbContext) : base(userInfo, dbContext)
    {}
    //...
}

服务:

public class ProductionService : Service, IProductionService {
        public ProductionService(IUserInfo userInfo, StoreDbContext dbContext)
            : base(userInfo, dbContext)
        {
        }
//...
}
public abstract class Service {        
    protected IProductionRepository m_productionRepository;
    public Service(IUserInfo userInfo, StoreDbContext dbContext)
    {
        UserInfo = userInfo;
        DbContext = dbContext;
    }
    protected IProductionRepository ProductionRepository
            => m_productionRepository ?? (m_productionRepository = new ProductionRepository(UserInfo, DbContext));
}

API:

  public class ProductionController : Controller {
        private readonly IUserInfo userInfo;
        protected IProductionService ProductionBusinessObject;
        public ProductionController(IUserInfo _userInfo, IProductionService productionBusinessObject)
        {
            userInfo = _userInfo;
            ProductionBusinessObject = productionBusinessObject;
        }
  }

现在,在我的Startup.cs中,我将JWT令牌与“ OnTokenValidated ”事件一起使用,以从令牌中获取UserInfo信息:

services.AddAuthentication(options =>
{
     options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
     options.Events = new JwtBearerEvents
     {
         #region Jwt After Validation Authenticated
         OnTokenValidated = async context =>
         {
              #region Get user's immutable object id from claims that came from ClaimsPrincipal
              var userID = context.Principal.Claims.Where(c => c.Type == ClaimTypes.NameIdentifier)
              services.Configure<UserInfo>(options =>
              {
                    options.UID = userID;
              });
              #endregion
          },
          #endregion
       }
};

我正在使用 services.Configure 并尝试将UID分配给IUserInfo对象,但是当我在控制器中进行调试时, IUserInfo始终代表一个空对象,例如在构造函数或api中方法。我知道我可能滥用了.Net核心中的依赖项注入,所以请随时指导我将IUserInfo注入到我的 Controller-> Service-> Repository 的正确方法是什么,所以他们都可以获取实际的UserInfo信息!

2 个答案:

答案 0 :(得分:5)

您可以通过将IUserInfo注册为“启动”中的服务来注入它。

services.AddScoped<IUserInfo>(provider =>
{
    var context = provider.GetService<IHttpContextAccessor>();

    return new UserInfo
    {
        UID = context.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier)
    };
});

答案 1 :(得分:3)

您以后不能再在服务集合中注册内容,尤其是不能针对某些请求进行动态注册。在ConfigureServices期间一次设置服务集合,然后将其冻结;您之后将无法修改。如果要在每个请求范围内使事物可用,则可以在请求范围的依赖项中将其作为状态共享,也可以将其放在HttpContext中。

尽管传递诸如DI依赖项之类的用户数据,这似乎是一个糟糕的设计。您应该考虑在方法调用中显式传递该信息。

此外,您应该真正接受声明并直接使用那个。您可以轻松地在ClaimsPrincipal上进行一些扩展方法,使您可以执行User.GetUserId()从声明中获取用户ID,而无需将其放入需要处理的自定义对象中。用户主体已经在整个框架中可用,因此只需使用它即可。


顺便说一句。请注意,通常使用services.Configure<UserInfo>()不会注册UserInfo依赖项(尤其不是IUserInfo依赖项!),而是配置IOptions<UserInfo>。但再次:在您的情况下,这将不起作用,因为在调用Configure()时,服务集合已经建立。