Как получить метод IOptions в ConfigureServices?

У меня есть основное приложение asp.net. Я хочу использовать шаблон IOptions для ввода значений из appsettings.json. Итак, у меня есть класс SecurityHeaderOptions, а также целевой класс SecurityHeadersBuilder, конструктор которого принимает IOptions<SecurityHeaderOptions> качестве параметра.

Я знаю, что ядро .net может неявно создавать экземпляр 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;
    }
}

Метод ConfigureServices

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?

Ответ 1

Используя .NET Core 2 и не имея поставщика (или заботясь о его добавлении) в ConfigureServices я решил использовать что-то вроде этого (используя OP-код в качестве примера):

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

    ...
}

Ответ 2

Вот так я регистрирую опции и внедряю их в 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();
    });
}

Ответ 3

По поводу ваших вопросов:

1. Да, вам нужно зарегистрировать параметры, но я считаю, что вы делаете это неправильно (по крайней мере, на вашем примере). Вы должны зарегистрироваться как это:

services.Configure<SecurityHeaderOptions>(options => Configuration.GetSection("SecurityHeaderOptions").Bind(options));

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