如何在ConfigureServices方法中获取IOptions?

问题描述 投票:0回答:7

我有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
的构造函数中?

c# asp.net-core .net-core asp.net-core-mvc
7个回答
94
投票

使用.NET Core 2并且在

ConfigureServices
中没有可用的提供程序(或愿意添加它),我选择使用类似的东西(使用OP代码作为示例):

public void ConfigureServices(IServiceCollection services)
{
    // secOpts available for use in ConfigureServices
    var secOpts = Configuration
        .GetSection("SecurityHeaderOptions")
        .Get<SecurityHeaderOptions>();

    ...
}

24
投票

这就是我注册选项并注入到

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();
    });
}

13
投票

文档具体说:

请勿在

IOptions<TOptions>
中使用
IOptionsMonitor<TOptions>
Startup.ConfigureServices
。由于服务注册的顺序,可能会存在不一致的选项状态。

因此,您必须通过

Startup.ConfigureServices
以其他方式访问配置,例如昆顿的回答


1
投票

关于您的问题:

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>();

1
投票

你可以做这样的事情

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());
}

0
投票

首先,您需要向

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();
});

0
投票
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;
© www.soinside.com 2019 - 2024. All rights reserved.