【问题标题】:How to get IOptions in ConfigureServices method?如何在 ConfigureServices 方法中获取 IOptions?
【发布时间】:2017-03-21 16:18:34
【问题描述】:

我有 asp.net 核心应用程序。我想使用IOptions 模式从 appsettings.json 注入值。所以我有一个类SecurityHeaderOptions,还有一个目标类SecurityHeadersBuilder,其构造函数以IOptions<SecurityHeaderOptions>为参数。

我知道 .net 核心可以在向容器注册后通过注入 IOptions<SecurityHeaderOptions> 来隐式创建 SecurityHeadersBuilder 的实例。

但是我想显式创建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> 如果我明确地将选项传递给构造函数,我是否需要使用service.Configure 方法向容器注册SecurityHeaderOptions

2> Configuration.GetSection("SecurityHeaderOptions") 不能返回 IOptions&lt;SecurityHeaderOptions&gt; 的实例,而是返回 IConfigurationSection?

3>无论哪种方式,我如何检索SecurityHeaderOptions 并将其传递给SecurityHeadersBuilder 的构造函数?

【问题讨论】:

    标签: c# asp.net-core .net-core asp.net-core-mvc


    【解决方案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();
          });
      }
      

      【讨论】:

      • 但是如果你只想执行一个方法,为什么要全局注册 SecurityHeadersBuilder 呢?只需调用构造函数上的方法:public SecurityHeadersBuilder(IOptions&lt;SecurityHeaderOptions&gt; options) { _options = options.Value; AddDefaultPolicy(); }
      • Builder 有多个策略,现在我正在使用默认策略。我将来可能会使用不同的策略。在这种情况下,我只需更改 startup.cs 即可调用不同的策略方法。
      • 好的。然后不要全局注册SecurityHeaderOptions,如果你不会在SecurityHeadersBuilder上注入它。当您编写代码时,您只是在实例化该类并将其全局注入。无需全局注册选项。 var option = Configuration.GetSection("SecurityHeaderOptions");
      • Configuration.GetSection("SecurityHeaderOptions") 不返回SecurityHeaderOptionsIOptions&lt;SecurityHeaderOptions&gt; 的实例请看我的第二个问题
      • 这很有帮助,谢谢 LP13
      【解决方案3】:

      Docs具体说:

      不要在Startup.ConfigureServices 中使用IOptions&lt;TOptions&gt;IOptionsMonitor&lt;TOptions&gt;。由于服务注册的顺序,可能存在不一致的选项状态。

      因此您必须通过Startup.ConfigureServices 以其他方式访问配置,例如Quinton's answer

      【讨论】:

        【解决方案4】:

        关于您的问题:

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

        【讨论】:

        • 正如我在帖子中提到的,.net core 可以进行隐式注入。但是我想在注册过程中调用AddDefaultPolicy() 方法。无论如何,我想我在进一步阅读后得到了答案。我将在下面发布我的答案。
        • 如果你只是想在注册时调用该方法,只需在构造函数上调用AddDefaultPolicy()即可。然后你不需要做任何其他事情,也不需要注册SecurityHeadersBuilder...
        【解决方案5】:

        首先,您需要向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&lt;T&gt;()扩展方法:

        services.AddScoped<SecurityHeadersBuilder>(provider =>
        {
            var options = Configuration.GetSection("SecurityHeaderOptions")
                .Get<SecurityHeaderOptions>();
        
            return new SecurityHeadersBuilder(options)
                .AddDefaultPolicy();
        });
        

        (然后您可以删除采用IOptions&lt;SecurityHeaderOptions&gt;SecurityHeadersBuilder 构造函数)。


        如果您需要在其他地方使用这些选项,则可以将上述方法与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();
        });
        

        【讨论】:

        • 当否决投票时,请花时间添加评论并向花时间提供答案的人简要解释您的理由。
        【解决方案6】:

        你可以这样做

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

        【讨论】:

          猜你喜欢
          • 2019-02-14
          • 2015-10-30
          • 2020-08-30
          • 1970-01-01
          • 1970-01-01
          • 2020-06-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多