【问题标题】:Get a service in a IServiceCollection extension在 IServiceCollection 扩展中获取服务
【发布时间】:2018-04-08 08:02:15
【问题描述】:

我有这个扩展

public static class ServiceCollectionExtensions
{
    public static IServiceCollection MyExtension(this IServiceCollection serviceCollection)
    {
      ...
    }
}

我需要从这样的服务中获取信息:

services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        var myService = <<HERE>>();
        options.TokenValidationParameters = this.GetTokenValidationParameters(myService);
    });

我该怎么做?

我尝试在var serviceProvider = services.BuildServiceProvider(); 之后获取ServiceProvider,然后我发送serviceProvider,但这不起作用..

【问题讨论】:

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


    【解决方案1】:

    在您调用services.AddSomething() 时,服务provider 尚未从服务集合构建。因此,您当时无法实例化服务。幸运的是,有一种方法可以在使用依赖注入的同时配置服务。

    当您执行services.AddSomething(options =&gt; …) 时,通常会在服务集合中注册一定数量的服务。然后传递过来的配置action也会以特殊的方式注册,这样以后服务实例化的时候,就可以执行那个配置动作来应用配置了。

    为此,您需要实现IConfigureOptions&lt;TOptions&gt;(或者实际上是IConfigureNamedOptions&lt;TOptions&gt; 用于身份验证选项)并将其注册为单例。出于您的目的,这可能如下所示:

    public class ConfigureJwtBearerOptions : IConfigureNamedOptions<JwtBearerOptions>
    {
        private readonly IMyService _myService;
    
        public ConfigureJwtBearerOptions(IMyService myService)
        {
            // ConfigureJwtBearerOptionsis constructed from DI, so we can inject anything here
            _myService = myService;
        }
    
        public void Configure(string name, JwtBearerOptions options)
        {
            // check that we are currently configuring the options for the correct scheme
            if (name == JwtBearerDefaults.AuthenticationScheme)
            {
                options.TokenValidationParameters = myService.GetTokenValidationParameters();
            }
        }
    
        public void Configure(JwtBearerOptions options)
        {
            // default case: no scheme name was specified
            Configure(string.Empty, options);
        }
    }
    

    然后您在 Startup 中注册该类型:

    services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        // add JwtBearer but no need to pass options here
        .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, configureOptions: null);
    
    // instead we are registering our configuration type to configure it later
    services.AddSingleton<IConfigureOptions<JwtBearerOptions>, ConfigureJwtBearerOptions>();
    

    这实际上与您执行 services.AddJwtBearer(scheme, options =&gt; { … }) 时发生的事情完全相同,只是抽象出来了,因此您无需关心它。但是通过手动操作,您现在拥有更多的权力和访问完整的依赖注入服务提供者的权限。

    【讨论】:

    • 谢谢,它工作正常。但我不得不更改为.AddJwtBearer();,因为AddJwtBearer 在扩展中没有AddJwtBearer(this AuthenticationBuilder builder, string authenticationScheme) 之类的重载。我不知道是否有办法像您通过 authenticationScheme 的示例一样。
    • @andresdescalzo 你是对的,只有身份验证方案名称但没有配置没有过载。您可以不传递任何内容(将使用默认方案名称),或者如果您需要指定方案名称,您可以传递 null 作为配置操作。我已经调整了上面的答案来解决这个问题。
    • 在核心 2.2 中,我不得不将 IConfigureNamedOptions 更改为 IConfigureOptions
    • 谢谢!最后是一个很好的例子,说明如何实际使用IConfigureNamedOptions 和设置身份验证!喜欢它!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-30
    • 1970-01-01
    • 2020-10-26
    • 2022-01-02
    • 1970-01-01
    • 2018-01-24
    相关资源
    最近更新 更多