我有一个问题与我的AspNet Core应用程序,我有一个扩展方法注册配置,在一个外部库,如在示例中:
public static AuthenticationBuilder AddSaml2PIdP(this AuthenticationBuilder builder, SamlSettings settings)
{
foreach (var idP in settings.IdPs)
{
builder.AddSaml2p(idP.Scheme, options =>
{
options.ServiceProviderOptions = SpOptionsHelper.GetSpOptions(settings);
....
}
}
return builder;
}
和在Startup类中,在ConfigureServices中,我这样调用这个方法:
var samlSettings = _configuration.GetSection(SamlSettings.SamlSection).Get<SamlSettings>();
services.AddAuthentication()
.AddSaml2PIdP(samlSettings);
现在不幸的是,我不得不做一些改变,所以我需要注入依赖关系,以便能够在该类中执行其他操作,所以我不能再使用静态类或扩展方法,我应该这样做:
public class Saml2PExtension
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public Saml2PExtension(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public void AddSaml2PIdP()
{
using var scope = _serviceScopeFactory.CreateScope();
var settings = scope.ServiceProvider.GetRequiredService<SamlSettings>();
var metadataParser = scope.ServiceProvider.GetRequiredService<ISamlMetadataParser>();
var environment = scope.ServiceProvider.GetRequiredService<IWebHostEnvironment>();
var builder = scope.ServiceProvider.GetRequiredService<AuthenticationBuilder>();
foreach (var idP in settings.IdPs)
{
builder.AddSaml2p(idP.Scheme, options =>
{
options.ServiceProviderOptions = SpOptionsHelper.GetSpOptions(settings);
....
}
}
}
}
但是我应该如何在ConfigureServices方法中注册这个类并调用configure方法?
使用新的实现,我不是使用ServiceProvider作为ServiceLocator反模式吗?
也欢迎能够以更优雅的方式解决问题的替代解决方案。
谢谢你
** EDIT **
最后,我选择保留第一个核心代码片段,将IWebHostEnvironment参数添加到扩展方法并手动实例化SamlMetadataParser,如下所示:public static AuthenticationBuilder AddSaml2PIdP(this AuthenticationBuilder builder, IWebHostEnvironment environment, SamlSettings settings)
{
var metadataParser = new SamlMetadataParser(new ...); // manually create object
foreach (var idP in settings.IdPs)
{
builder.AddSaml2p(idP.Scheme, options =>
{
options.ServiceProviderOptions = SpOptionsHelper.GetSpOptions(settings);
// Read metadata from file ...
var idPOptions = metadataParser.Pars(xml);
........
}
}
return builder;
}
既然我在作文根,我认为这种方法是正确的。
谢谢你的帮助
这可以通过构建您自己的(只使用一次)容器来获取您注册的服务。您可以保留您的扩展方法,因为我们有一个通过AuthenticationBuilder
暴露的IServiceCollection
,这非常方便:
public static AuthenticationBuilder AddSaml2PIdP(this AuthenticationBuilder builder,
SamlSettings settings)
{
using var scope = builder.Services.BuildServiceProvider().CreateScope();
//get the services
var settings = scope.ServiceProvider.GetRequiredService<SamlSettings>();
var metadataParser = scope.ServiceProvider.GetRequiredService<ISamlMetadataParser>();
var environment = scope.ServiceProvider.GetRequiredService<IWebHostEnvironment>();
var builder = scope.ServiceProvider.GetRequiredService<AuthenticationBuilder>();
//start using the services
foreach (var idP in settings.IdPs)
{
builder.AddSaml2p(idP.Scheme, options =>
{
options.ServiceProviderOptions = SpOptionsHelper.GetSpOptions(settings);
....
}
}
return builder;
}
请注意,这是可能的,但你需要关心你所使用的服务所需的所有依赖项的注册顺序。实际上,您可以将AddAuthentication
放在ConfigureServices
的末尾,以确保它总是成功的。但是,如果有任何错误,将抛出异常,我们可以相应地调整顺序。
:
关于在IdentityServer4中可能与IdpOptions
一起使用的后配置(用于OP尝试):
services.AddOptions<IdpOptions>()
.Configure((IdpOptions o,
SamlSettings settings,
ISamlMetadataParser metadataParser,
IWebHostEnvironment environment,
AuthenticationBuilder builder) => {
//use your services here to configure next ...
});
实际上上面使用了OptionsBuilder
,它支持最多注入5个依赖项。您可以使用一个单独的类来实现IConfigureOptions<IdpOptions>
或IPostConfigureOptions<IdpOptions>
。然后用services.ConfigureOptions<TImplementation>()
配置它。