【问题标题】:.Net Core - Inject Dependency IUserInfo from API middleware down to repository layer.Net Core - 从 API 中间件注入依赖 IUserInfo 到存储库层
【发布时间】:2023-03-24 05:56:01
【问题描述】:

假设我从下到上有以下结构化项目层,如 Repository -> Service -> 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 中,我使用带有“OnTokenValidated”事件的 JWT 令牌从令牌中获取 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信息!

【问题讨论】:

  • 难道不需要注册 UserInfo 来进行 Scoped 操作吗?我可能错过了它,但我没有看到任何类似的东西。
  • 建议审查当前的设计选择。配置后构建的服务不会受到在请求期间尝试添加其他服务的影响。
  • @DavidL:对不起,我忘记粘贴了,注册就像 services.AddScoped();在 ConfigureServices 方法中。

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


【解决方案1】:

您可以通过在 Startup 中将 IUserInfo 注册为服务来注入它。

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

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

【讨论】:

  • 哇!这是一个很棒的解决方案!非常感谢!
  • 我对 HttpContext.User 背后的理论很好奇,我通过 Claim 设置我的 UID 或电子邮件或名称,然后分配给 JWT 安全令牌,但是 asp.net 核心是如何知道 HttpContext.User也设置了相同的信息?看来我必须在我的 api 上使用“授权”属性,这样我才能获得 IUserInfo,如果我没有,我的 API 将无法检索在 ConfigureService() 中设置的 IUserInfo,所以是否有任何连接在这些之间?我实际上正在关注一个在线教程,但我确实不知道这种情况背后的想法......
  • @KevinDing 你应该阅读官方文档的authentication chapter。您正在使用 JWT 身份验证,因此在每次请求之前,都会有中间件解析和验证 JWT 令牌,然后使 HttpContext.User 可用。
  • @poke:非常感谢,我会通过这个来更好地理解!
  • @KevinDing 您可以通过在您的操作方法和控制器上使用[AllowAnonymous] 属性来绕过授权。 IUserInfo 仍将由 DI 解决,但 IUserInfo.UID 属性将为空(或在您的情况下为零)。只有当您拥有不需要知道用户是谁的公开可用 API 端点时,您才会这样做。
【解决方案2】:

您不能像以后那样在服务集合中注册东西,尤其是对于某些请求不能动态注册。服务集合被设置一次(在ConfigureServices期间)然后冻结;你以后不能修改它。如果您想在每个请求范围内提供可用的东西,您可以在请求范围的依赖项中将其作为状态共享,或者将其放在 HttpContext 上。

虽然将这样的用户数据作为 DI 依赖项来传递,但这似乎是一个糟糕的设计。您应该考虑在方法调用中显式传递该信息。

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


顺便说一句。请注意,使用services.Configure&lt;UserInfo&gt;() 通常不会注册UserInfo 依赖项(尤其不是IUserInfo 依赖项!),而是会配置IOptions&lt;UserInfo&gt;。但同样:这在您的情况下不起作用,因为在调用 Configure() 时,服务集合已经构建完毕。

【讨论】:

  • 感谢您的建议!只是想确认一下,所以您建议使用 HttpContext 或请求对象通过 API 方法获取或分配用户信息,或者只使用 User.ClaimPrincipal,对吗?但是,假设我可以在 API 方法中获取 UID,我仍然停留在如何传递它或让我的服务(IProductionService)包含或附加这个 IUserInfo 到它?我不知道如何通过使用 API 控制器层中的 DI 来实现它...
  • 你的控制器有 User 对象(主体),所以当你想为那个用户做某事时,你应该明确地传递它。让用户被 DI 注入(以任何方式)只会将您锁定在仅适用于当前用户的功能中。
猜你喜欢
  • 2018-06-20
  • 2023-02-24
  • 2016-05-22
  • 1970-01-01
  • 2020-08-11
  • 1970-01-01
  • 1970-01-01
  • 2021-07-15
  • 2021-10-23
相关资源
最近更新 更多