我有asp.net core应用程序。我想使用
IOptions
模式从 appsettings.json 注入值。所以我有一个类 SecurityHeaderOptions
,还有目标类 SecurityHeadersBuilder
,其构造函数以 IOptions<SecurityHeaderOptions>
作为参数。
我知道 .net core 可以通过在容器注册后注入
SecurityHeadersBuilder
来隐式创建 IOptions<SecurityHeaderOptions>
的实例。
但是我想显式创建
SecurityHeadersBuilder
的实例,调用其方法之一,然后向容器注册该实例。
public sealed class SecurityHeaderOptions
{
public string FrameOption { get; set; }
public string XssProtection { get; set; }
}
public class SecurityHeadersBuilder
{
private readonly SecurityHeaderOptions _options = null;
public SecurityHeadersBuilder(IOptions<SecurityHeaderOptions> options)
{
_options = options.Value;
}
public SecurityHeadersBuilder AddDefaultPolicy()
{
AddFrameOptions();
AddConetntSecurityPolicy();
return this;
}
}
配置服务方法
public void ConfigureServices(IServiceCollection services)
{
services.Configure<SecurityHeaderOptions>(Configuration.GetSection("SecurityHeaderOptions"));
services.AddScoped<SecurityHeadersBuilder>(provider =>
new SecurityHeadersBuilder(?????).AddDefaultPolicy())
}
问题
1> 如果我显式地将选项传递给构造函数,我是否需要使用
SecurityHeaderOptions
方法向容器注册 service.Configure
?
2>
Configuration.GetSection("SecurityHeaderOptions")
无法返回 IOptions<SecurityHeaderOptions>
的实例,而是返回 IConfigurationSection
?
3>无论哪种方式,我如何检索并将
SecurityHeaderOptions
传递到 SecurityHeadersBuilder
的构造函数中?
使用.NET Core 2并且在
ConfigureServices
中没有可用的提供程序(或愿意添加它),我选择使用类似的东西(使用OP代码作为示例):
public void ConfigureServices(IServiceCollection services)
{
// secOpts available for use in ConfigureServices
var secOpts = Configuration
.GetSection("SecurityHeaderOptions")
.Get<SecurityHeaderOptions>();
...
}
这就是我注册选项并注入到
SecurityHeadersBuilder
的方式
public void ConfigureServices(IServiceCollection services)
{
services.Configure<SecurityHeaderOptions>(Configuration.GetSection("SecurityHeaderOptions"));
services.AddScoped<SecurityHeadersBuilder>(provider =>
{
var option = provider.GetService<IOptions<SecurityHeaderOptions>>();
return new SecurityHeadersBuilder(option)
.AddDefaultPolicy();
});
}
关于您的问题:
1.是的,您需要注册选项,但我相信您的做法是错误的(至少按照您的示例)。您应该这样注册:
services.Configure<SecurityHeaderOptions>(Configuration.GetSection("SecurityHeaderOptions"));
2.我相信我上面提到的正确注册会返回您所期望的结果。
3. 只需注册它并将其放在
SecurityHeaderBuilder
构造函数上就足够了。注册时不需要(默认的 .NET Core IOC 容器也不允许)传递构造函数参数。为此,您需要使用其他 IOC,例如 Autofac。
但是您需要注册
SecurityHeadersBuilder
才能在其他课程中使用它。
只需使用一个接口即可。
public interface ISecurityHeadersBuilder
{
SecurityHeadersBuilder AddDefaultPolicy();
}
public class SecurityHeadersBuilder : ISecurityHeadersBuilder
{
private readonly SecurityHeaderOptions _options = null;
public SecurityHeadersBuilder(IOptions<SecurityHeaderOptions> options)
{
_options = options.Value;
}
public SecurityHeadersBuilder AddDefaultPolicy()
{
AddFrameOptions();
AddContentSecurityPolicy();
return this;
}
}
然后,只需在
startup.cs
中注册即可,就像这样
services.AddScoped<ISecurityHeadersBuilder, SecurityHeadersBuilder>();
你可以做这样的事情
public static class IConfigurationExtensions
{
public static TypedConfiguration<SecurityHeaderOptions> GetSecurityHeaderOptions(this IConfiguration configuration)
{
return new TypedConfiguration<SecurityHeaderOptions>(configuration.GetSection("SecurityHeaderOptions"));
}
}
public class TypedConfiguration<T> where T : class
{
public TypedConfiguration(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public T Value => Configuration.Get<T>();
public void InitializeOptions(IServiceCollection services)
{
services.Configure<T>(Configuration);
}
}
现在,您已经从一个位置创建了具有
IConfiguration
、键入 SecurityHeaderOptions
和用于为该类注册 IOptions 注入的辅助方法的对象。
像这样使用它
public void ConfigureServices(IServiceCollection services)
{
var wrappedOptions = Configuration.GetSecurityHeaderOptions();
wrappedOptions.InitializeOptions(services);
var options = Options.Create(wrappedOptions.Value);
services.AddScoped<SecurityHeadersBuilder>(provider =>
new SecurityHeadersBuilder(options).AddDefaultPolicy());
}
首先,您需要向
SecurityHeadersBuilder
添加第二个构造函数,它需要一个普通的 SecurityHeaderOptions
:
public class SecurityHeadersBuilder
{
private readonly SecurityHeaderOptions _options;
public SecurityHeadersBuilder(IOptions<SecurityHeaderOptions> options)
{
_options = options.Value;
}
public SecurityHeadersBuilder(SecurityHeaderOptions options)
{
_options = options;
}
public SecurityHeadersBuilder AddDefaultPolicy()
{
AddFrameOptions();
AddContentSecurityPolicy();
return this;
}
}
那么答案完全取决于您是否需要在
Startup
类之外使用这些选项。
如果没有,您可以简单地使用
Microsoft.Extensions.Configuration.ConfigurationBinder.Get<T>()
扩展方法:
services.AddScoped<SecurityHeadersBuilder>(provider =>
{
var options = Configuration.GetSection("SecurityHeaderOptions")
.Get<SecurityHeaderOptions>();
return new SecurityHeadersBuilder(options)
.AddDefaultPolicy();
});
(然后您可以删除采用
SecurityHeadersBuilder
的 IOptions<SecurityHeaderOptions>
构造函数)。
如果您需要在其他地方使用这些选项,那么您可以将上述方法与
Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure()
扩展方法结合起来:
var optionsSection = Configuration.GetSection("SecurityHeaderOptions");
services.Configure<SecurityHeaderOptions>(optionsSection);
services.AddScoped<SecurityHeadersBuilder>(provider =>
{
var options = optionsSection.Get<SecurityHeaderOptions>();
return new SecurityHeadersBuilder(options)
.AddDefaultPolicy();
});
SecurityHeaderOptions securityHeaderOptions = new SecurityHeaderOptions();
services.AddOptions<SecurityHeaderOptions>().Configure<IConfiguration>((options, config) =>
{
IConfigurationSection section = config.GetSection("SecurityHeaderOptions");
securityHeaderOptions = section.Get<SecurityHeaderOptions>();
section.Bind(options);
});
securityHeaderOptions.YourVariable;