在哪里存储我需要的多个级别的许可证信息?

Where to store license information that I will need at multiple levels?

我正在开发 .Net 6 API。

我的项目包括控制器、服务和存储库(使用依赖注入)。

我还通过中间件添加了权限检查:

Program.cs

app.UseMiddleware<AuthMiddleware>();

AuthMiddleware.cs

public class AuthMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<EcmAuthMiddleware> _logger;
    private readonly IConfiguration _config;

    public AuthMiddleware(RequestDelegate next, 
      ILogger<AuthMiddleware> logger, IConfiguration config)
    {
       _next = next;
       _logger = logger;
       _config = config;
    }

    public async Task Invoke(HttpContext context, IUserApiService 
     userApiService)
    {
        ...
        context.Items["Instance"] = instance;
        await _next(context);
    }
}

从这里我将客户(和数据库)带到 运行 API 上。

现在我需要从新获得的客户端获取一些许可信息(通过外部 API)并将其存储在某个地方。

我尝试从控制器调用调用,但几乎所有控制器都必须重复调用。于是想到把调用转给中间件

从通话中我将获得各种信息,我想存储这些信息以供底层使用:控制器、服务和存储库。我不想使用会话或 cookie。

我可以只使用 httpcontext 还是有其他解决方案?

context.Items["LicenseInfo"] = licenseInfo;

此信息仅对 api 的调用有效,不应存储(例如应用程序)。

编辑: GetLicenseInfo() 必须包含外部调用:

string result = await _userApiService.GetUserApiResponseAsString("users/token", HttpMethod.Get, applicationId, token);

这只是 MiddleWare 的替代品,它可能是更好的选择,也可能不是,它取决于很多因素(如大多数软件开发问题)。

这可能是工厂:

public class LicenseInfoFactory
{
    public LicenseInfoFactory(IHttpContextAccessor context)
    {
      _context = context;
    }

    private readonly IHttpContextAccessor _context;

    public LicenseInfo Build()
    {
        _context...
        // api call to get/build/return LicenseInfo
    }
}

然后在您的启动中:

services.AddHttpContextAccessor();
services.AddSingleton<LicenseInfoFactory>();
services.AddScoped<LicenseInfo>(provider => { 
   var factory = provider.GetRequiredService<LicenseInfoFactory>();
   return factory.Build();
});

然后只需在 controllers/repositories 构造函数等中注入 LicenseInfo

public MyController(LicenseInfo licenseInfo)

这应该只会进行一次 api 调用 per scoped request。 (凭记忆,语法可能不准确)

Can I use only httpcontext or are there other solutions?

为此使用 HttpContext.Items 没有错。这正是 HttpContext.Items 因为: 将上下文数据附加到 HTTP 请求。对于这种“对象字典”API,为了类型安全和简单,我喜欢用自己的 API 包裹它:

public static class HttpContextLicenseInfoExtensions
{
  public static void SetLicenceInfo(this HttpContext context, LicenseInfo licenseInfo) =>
      context.Items[key] = licenseInfo;
  public static LicenseInfo? TryGetLicenseInfo(this HttpContext context) =>
      context.Items[key] as LicenseInfo;
  public static LicenseInfo GetLicenseInfo(this HttpContext context) =>
      context.TryGetLicenseInfo() ?? throw new InvalidOperationException("No license info.");

  private static readonly string key = Guid.NewGuid().ToString("N");
}

// Example middleware
app.Use(async (context, next) =>
{
  context.SetLicenseInfo(licenseInfo);
  await next.Invoke();
});

// Example usage
var licenseInfo = HttpContext.GetLicenseInfo();

但是如果你真的想要避免HttpContext.Items,你可以使用AsyncLocal<T>。您只想构造 API 以便为特定范围设置值(我喜欢 return IDisposable 到 un-set 值),然后通常注入读取当前值的“访问器”。像这样的东西应该可以工作(使用我的一次性图书馆中的 Disposable):

public static class AsyncLocalLicenseInfo
{
  public static IDisposable Set(LicenseInfo licenseInfo)
  {
    var originalValue = local.Value;
    local.Value = licenseInfo;
    return new Disposable(() => local.Value = originalValue);
  }

  public static LicenseInfo? TryGet() => local.Value;

  public static LicenseInfo LicenseInfo => TryGet() ?? throw new InvalidOperationException("No license info.");

  private static readonly AsyncLocal<LicenseInfo> local = new();
}

// Example middleware
app.Use(async (context, next) =>
{
  using var localValue = AsyncLocalLicenseInfo.Set(licenseInfo);
  await next.Invoke();
});

// Example usage
var licenseInfo = AsyncLocalLicenseInfo.LicenseInfo;

如果您不喜欢 static API,您可以将其隐藏在“访问器”后面:

// Inject this into downstream types
public interface ILicenseInfoAccessor
{
  LicenseInfo LicenseInfo { get; }
}

public sealed class LicenseInfoAccessor : ILicenseInfoAccessor
{
  public LicenseInfo LicenseInfo => AsyncLocalLicenseInfo.LicenseInfo;
}

// Example usage
var licenseInfo = licenseInfoAccessor.LicenseInfo;