这感觉像是一个简单的问题要解决,但在我所有的搜索中,我仍然没有找到一个适合我的解决方案。可能是另一个没有找到我想要的东西的情况,因为我没有搜索正确的"东西",但我们在这里…
我有一个c# Web API程序,我想从一个配置对象配置红隼服务器。
我通过rest调用接收这个配置到我的服务中,到一个CustomConfig
对象中。我可以在Program.cs
或Startup.cs
中获得此配置对象,但由于我不想重复自己并进行额外的调用,所以我不想在两个地方都这样做。
我的偏好是在Startup.cs
中获得配置,因为这是我其余的配置代码所在的地方,并且是我已经使用CustomConfig
对象的地方。然而,我找不到一种方法来配置红隼服务器使用我给它的证书(在Startup.cs中),我也看不到从Program.cs
注入此配置到Startup.cs
的方法。
在其他项目中,我已经将PFX文件的位置传递为环境变量:ASPNETCORE_Kestrel__Certificates__Default__Path
(在这种情况下,一切都可以在没有额外代码配置的情况下工作),但在这个项目中,所有配置必须通过rest调用检索,所以这不是这里的选项。
我目前有一切运行,但只有通过使rest调用得到配置两次。当前配置kestrel的实现是将PFX作为base64字符串存储在CustomConfig
中,并在Program.cs
中配置:
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
CustomConfig config = CustomConfig() // <- I receive config here
webBuilder.UseStartup<Startup>();
webBuilder.UseKestrel(options =>
{
options.ConfigureHttpsDefaults(d =>
{
byte[] pfxBytes = Convert.FromBase64String(config.Base64PFXBytes);
d.ServerCertificate = new X509Certificate2(pfxBytes, "sslKey");
});
});
});
}
总结。
- 我有一个
CustomConfig
对象,用于配置Startup.cs
中的服务 - 我想从我的
CustomConfig
配置我的红隼服务器
所以我在寻找帮助:
- 让红隼在
Startup.cs
内使用我的PFX - 将
CustomConfig
对象从Program.cs
传入Startup.cs
希望这是有意义的…欢迎任何&所有解决方案/附加问题的清晰度!
提前感谢!
. NET Core使用IConfiguration
接口抽象配置。不深入细节,它收集来自各种IConfigurationSource
的配置,并将它们堆叠在一起,这让我们可以覆盖a通过使用相同的键来定义在一个源中定义的值,从而在另一个源中设置。
1。实现IConfigurationSource
让我们实现一个IConfigurationSource
。我们可以使用ConfigurationSource
抽象类作为我们的起点。我们会使用内存实现,然后切换到远程源。
class RemoteConfigurationSource : IConfigurationSource
{
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new RemoteConfigurationProvider(_options);
}
private class RemoteConfigurationProvider : ConfigurationProvider
{
public override void Load()
{
// TODO: fetch data from the API
var remoteConfig = new Dictionary<string, string>
{
{ "CertificateOptions:PfxBase64", "MIIKkQIBAz....gfQ" },
{ "CertificateOptions:Password", "secret" },
};
Data = remoteConfig;
}
}
}
然后将此添加到ConfigureHostConfiguration
回调中的配置构建器中:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureHostConfiguration(builder =>
{
// add new source
builder.AddRemoteConfiguration();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>().UseKestrel((context, options) =>
{
var certificateOptions = context.Configuration
.GetSection(KestrelCertificateOptions.ConfigurationKey)
.Get<KestrelCertificateOptions>();
options.ConfigureHttpsDefaults(adapterOptions =>
adapterOptions.ServerCertificate = certificateOptions.Certificate);
});
});
}
public static class ConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddRemoteConfiguration(this IConfigurationBuilder builder) =>
builder.Add(new RemoteConfigurationSource());
}
class KestrelCertificateOptions
{
public const string ConfigurationKey = "CertificateOptions";
public string PfxBase64 { get; set; }
public string Password { get; set; }
public X509Certificate2 Certificate => new X509Certificate2(Convert.FromBase64String(PfxBase64), Password);
}
当我们运行应用程序时,ASP。. NET Core将加载并使用我们的内存配置。
2。从API获取配置数据
现在让我们从远程API获取配置。它需要返回配置值,节名用分隔符分隔带有冒号:
。以下是与JSON相同的配置,在CertificateOptions
部分下归档:
{
"CertificateOptions:PfxBase64": "MII....oCAgfQ",
"CertificateOptions:Password": "secret"
}
假设API包装返回的数据包装为:
{
"Application": "MyApp",
"LastChanged": "2021-08-09 14:38:00",
"Data": {
"CertificateOptions:PfxBase64": "MIIK...oCAgfQ",
"CertificateOptions:Password": "secret"
}
}
所以在获取数据时我们只需要考虑Data
键
class RemoteConfigurationSource : IConfigurationSource
{
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new RemoteConfigurationProvider();
}
private class RemoteConfigurationProvider : ConfigurationProvider
{
public override void Load()
{
// We cannot await this method, so have to do sync-over-async.
// Not an issue, because it's a one-time thing.
var result = LoadRemoteConfig().GetAwaiter().GetResult();
Data = result.Data;
}
private async Task<RemoteConfigResult> LoadRemoteConfig()
{
// We cannot use IHttpClientFactory here, since ServiceProvider isn't even built yet.
using var httpClient = new HttpClient();
// ... add headers, token to request
return await httpClient.GetFromJsonAsync<RemoteConfigResult>("https://example.com/path/to/json");
}
}
private class RemoteConfigResult
{
public Dictionary<string, string> Data { get; set; }
}
}
为了稍微清理一下,我们可以将URL和其他凭证移动到appsettings.json:
{
"Logging": {
/*...*/
},
"RemoteConfiguration": {
"Url": "https://jsonkeeper.com/b/B78I",
"ApplicationId": "myconfigappid",
"Secret": "myconfigapisecret"
}
}
然后构建一个临时的IConfiguration
,添加尽可能多的源,然后获取这些值:
// Read credentials from appsettings.json
var remoteConfigurationOptions = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false)
.Build()
.GetSection(RemoteConfigurationOptions.ConfigurationKey)
.Get<RemoteConfigurationOptions>();
public class RemoteConfigurationOptions
{
public const string ConfigurationKey = "RemoteConfiguration";
public string Url { get; set; }
public string ApplicationId { get; set; }
public string Secret { get; set; }
}
然后将该对象传递给配置源,配置源再将其传递给配置提供程序