【问题标题】:How do you get IOptions<T> values from services?如何从服务中获取 IOptions<T> 值?
【发布时间】:2020-06-25 21:54:05
【问题描述】:

我在开发时尝试做 TDD,但我很挣扎。我的第一个测试我可以成功获得在 services.AddAuthentication(...) 中设置的选项,但是我无法从添加到上一个调用中的 .AddCookie(..) 中获得选项。在调试期间,我确实看到为 CookieAuthenticationOptions 添加了一个 IPostConfigureOptions,我怀疑它以某种方式更改了默认选项值,但我不知道如何获取它。

要测试的代码:

public static void AddOpenIdConnect(this IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options => { options.MinimumSameSitePolicy = SameSiteMode.None; });

    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie(
        CookieAuthenticationDefaults.AuthenticationScheme,
        options =>
        {
            options.ExpireTimeSpan = TimeSpan.FromHours(2);
            options.SlidingExpiration = true;
            options.AccessDeniedPath = new PathString("/Error/403");
        }
    );
}

我的第一个测试确保设置了 AuthenticationOptions,并且通过了:

[TestMethod]
public void AddOpenIdConnect_Should_AddAuthenticationOptions()
{
    //  arrange
    var services = new ServiceCollection();

    //  act
    services.AddOpenIdConnect();

    //  assert
    var provider = services.BuildServiceProvider();
    var authOptions = provider.GetRequiredService<IOptions<AuthenticationOptions>>();

    authOptions.Value.DefaultScheme.Should().Be(CookieAuthenticationDefaults.AuthenticationScheme);
    authOptions.Value.DefaultChallengeScheme.Should().Be(OpenIdConnectDefaults.AuthenticationScheme);
}

此方法失败,因为 AccessDeniedPath 返回默认设置,而不是正在测试的代码设置的设置(您可以看到我注释掉了对 ExpireTimeSpan 的检查,因为它也失败了。)

[TestMethod]
public void AddOpenIdConnect_Should_AddCookieAuthenticationOptions()
{
    //  arrange
    var services = new ServiceCollection();

    //  act
    services.AddOpenIdConnect();

    //  assert
    var provider = services.BuildServiceProvider();
    var authCookieOptions = provider.GetRequiredService<IOptions<CookieAuthenticationOptions>>();

    //authCookieOptions.Value.ExpireTimeSpan.Should().Be(TimeSpan.FromHours(2));
    authCookieOptions.Value.SlidingExpiration.Should().BeTrue();
    authCookieOptions.Value.AccessDeniedPath.Should().Be("/Error/403");
}

任何帮助将不胜感激,因为在此之后我仍然需要添加“.AddOpenIdConnect(options =>”。

谢谢!

【问题讨论】:

    标签: c# unit-testing asp.net-core dependency-injection


    【解决方案1】:

    AddCookie 扩展名的source code 表明它在添加CookieAuthenticationOptions 时使用身份验证方案配置命名选项

    public static AuthenticationBuilder AddCookie(this AuthenticationBuilder builder, 
    string authenticationScheme, string displayName, Action<CookieAuthenticationOptions> configureOptions)
    {
        builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<CookieAuthenticationOptions>, PostConfigureCookieAuthenticationOptions>());
        builder.Services.AddOptions<CookieAuthenticationOptions>(authenticationScheme).Validate(o => o.Cookie.Expiration == null, "Cookie.Expiration is ignored, use ExpireTimeSpan instead.");
        return builder.AddScheme<CookieAuthenticationOptions, CookieAuthenticationHandler>(authenticationScheme, displayName, configureOptions);
    }
    

    Source Code

    注意

    builder.Services.AddOptions<CookieAuthenticationOptions>(authenticationScheme) //<-- Named option
    

    因此,需要使用注册选项时使用的相同方案才能访问名称选项

    这可以使用IOptionsSnapshot&lt;TOptions&gt;.Get(String) Method来完成

    //...omitted for brevity
    
    // Assert
    var provider = services.BuildServiceProvider();
    IOptionsSnapshot<CookieAuthenticationOptions> namedOptionsAccessor = 
        provider.GetRequiredService<IOptionsSnapshot<CookieAuthenticationOptions>>();
    
    CookieAuthenticationOptions options = 
        namedOptionsAccessor.Get(CookieAuthenticationDefaults.AuthenticationScheme);
    
    options.ExpireTimeSpan.Should().Be(TimeSpan.FromHours(2));
    options.SlidingExpiration.Should().BeTrue();
    options.AccessDeniedPath.Should().Be("/Error/403");
    

    【讨论】:

    • 谢谢@Nkosi。我没有意识到我必须使用 IOptionsSnapshot 来获取值,因为我还没有对 Options 模式做很多事情。
    猜你喜欢
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-08
    • 1970-01-01
    相关资源
    最近更新 更多