在哪里存储我需要的多个级别的许可证信息?
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;
我正在开发 .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;