如何在 ASP.NET Core 5 运行时动态重新配置服务?
How can I dynamically re-configure a Service during runtime in ASP.NET Core 5?
我在我的网络应用程序中使用 Google 身份验证,并且 OAuth 密钥当前在 ConfigureServices 中进行了硬编码:
services.AddAuthentication()
.AddGoogle(options =>
{
options.ClientId = "my-client-id";
options.ClientSecret = "my-client-secret";
});
但是,我想让站点管理员有机会从 Web 应用程序的设置页面更改 ClientId 和 ClientSecret,最好不必重新启动服务器。
为此,当用户在设置页面上点击 'Save' 时,我必须以某种方式触发 Google 服务和 GoogleOptions 对象的重新配置.这就是我遇到的麻烦。另外,我想将这些设置存储在 EF Core DbContext 中,而不是物理配置文件中。
到目前为止,我已尝试将设置移动到实现 IPostConfigureOptions 的单独 class。这应该允许我注入我的数据库上下文,因为根据文档,PostConfigure 应该在所有其他配置发生后 运行。从这个新 class 正确加载设置,但数据库上下文注入失败,出现以下异常:
System.InvalidOperationException: Cannot consume scoped service 'AppDatabase' from singleton 'IOptionsMonitor`1[GoogleOptions]'
这很奇怪,因为 ConfigureGoogleOptions 被注册为 Scoped,而不是 Singleton。
这是我的选择 class:
public class ConfigureGoogleOptions : IPostConfigureOptions<GoogleOptions>
{
private readonly AppDatabase database;
public ConfigureGoogleOptions(AppDatabase database)
{
this.database = database;
}
public void PostConfigure(string name, GoogleOptions options)
{
options.ClientId = "my-client-id.apps.googleusercontent.com";
options.ClientSecret = "my-client-secret";
}
}
并在 ConfigureServices 中注册:
services.AddScoped<IPostConfigureOptions<GoogleOptions>, ConfigureGoogleOptions>();
即使数据库注入有效,仍然存在第二个问题。我的 class 中的 PostConfigure
函数仅在应用程序启动后被调用一次,并且再也不会调用。我假设它将设置缓存在某处,但我不知道如何使该缓存失效或禁用,以便我可以动态提供值。
简短摘要/tl;dr:
我想从我自己的数据库加载 Google OAuth 服务的 ClientId 和 ClientSecret 设置,并且我希望能够在服务器 运行ning 时动态更改它们。
内部 google 处理程序将使用 IOptionsMonitor<GoogleOptions>
获取 GoogleOptions
一次 或在重新加载之前(例如,从配置文件绑定选项并保存文件时,将触发重新加载)。 IOptionsMonitor
内部将使用 IOptionsMonitorCache
并且此缓存被注册为单例。因此,您从 IOptionsMonitor<GoogleOptions>
获得的选项实例与用于处理程序内部各种操作的 AuthenticationHandler<GoogleOptions>.Options
相同(参考)。即使使用该选项的其他代码也应该从 IOptionsMonitor<GoogleOptions>
.
正确获取它
所以要在运行时更改选项,就像这样简单:
//inject the IOptionsMonitor<GoogleOptions> into _googleOptionsMonitor;
var runtimeOptions = _googleOptionsMonitor.Get(GoogleDefaults.AuthenticationScheme);
//you change properties of runtimeOptions here
//...
这里的重点是我们需要使用 GoogleDefaults.AuthenticationScheme
作为键来获取正确的选项实例。 IOptionsMonitor.CurrentValue
将使用默认键 Options.DefaultName
(这是一个空字符串)。
我在我的网络应用程序中使用 Google 身份验证,并且 OAuth 密钥当前在 ConfigureServices 中进行了硬编码:
services.AddAuthentication()
.AddGoogle(options =>
{
options.ClientId = "my-client-id";
options.ClientSecret = "my-client-secret";
});
但是,我想让站点管理员有机会从 Web 应用程序的设置页面更改 ClientId 和 ClientSecret,最好不必重新启动服务器。
为此,当用户在设置页面上点击 'Save' 时,我必须以某种方式触发 Google 服务和 GoogleOptions 对象的重新配置.这就是我遇到的麻烦。另外,我想将这些设置存储在 EF Core DbContext 中,而不是物理配置文件中。
到目前为止,我已尝试将设置移动到实现 IPostConfigureOptions 的单独 class。这应该允许我注入我的数据库上下文,因为根据文档,PostConfigure 应该在所有其他配置发生后 运行。从这个新 class 正确加载设置,但数据库上下文注入失败,出现以下异常:
System.InvalidOperationException: Cannot consume scoped service 'AppDatabase' from singleton 'IOptionsMonitor`1[GoogleOptions]'
这很奇怪,因为 ConfigureGoogleOptions 被注册为 Scoped,而不是 Singleton。
这是我的选择 class:
public class ConfigureGoogleOptions : IPostConfigureOptions<GoogleOptions>
{
private readonly AppDatabase database;
public ConfigureGoogleOptions(AppDatabase database)
{
this.database = database;
}
public void PostConfigure(string name, GoogleOptions options)
{
options.ClientId = "my-client-id.apps.googleusercontent.com";
options.ClientSecret = "my-client-secret";
}
}
并在 ConfigureServices 中注册:
services.AddScoped<IPostConfigureOptions<GoogleOptions>, ConfigureGoogleOptions>();
即使数据库注入有效,仍然存在第二个问题。我的 class 中的 PostConfigure
函数仅在应用程序启动后被调用一次,并且再也不会调用。我假设它将设置缓存在某处,但我不知道如何使该缓存失效或禁用,以便我可以动态提供值。
简短摘要/tl;dr:
我想从我自己的数据库加载 Google OAuth 服务的 ClientId 和 ClientSecret 设置,并且我希望能够在服务器 运行ning 时动态更改它们。
内部 google 处理程序将使用 IOptionsMonitor<GoogleOptions>
获取 GoogleOptions
一次 或在重新加载之前(例如,从配置文件绑定选项并保存文件时,将触发重新加载)。 IOptionsMonitor
内部将使用 IOptionsMonitorCache
并且此缓存被注册为单例。因此,您从 IOptionsMonitor<GoogleOptions>
获得的选项实例与用于处理程序内部各种操作的 AuthenticationHandler<GoogleOptions>.Options
相同(参考)。即使使用该选项的其他代码也应该从 IOptionsMonitor<GoogleOptions>
.
所以要在运行时更改选项,就像这样简单:
//inject the IOptionsMonitor<GoogleOptions> into _googleOptionsMonitor;
var runtimeOptions = _googleOptionsMonitor.Get(GoogleDefaults.AuthenticationScheme);
//you change properties of runtimeOptions here
//...
这里的重点是我们需要使用 GoogleDefaults.AuthenticationScheme
作为键来获取正确的选项实例。 IOptionsMonitor.CurrentValue
将使用默认键 Options.DefaultName
(这是一个空字符串)。