在 IServiceCollection 扩展中获取服务

Get a service in a IServiceCollection extension

我有这个扩展

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,但这不起作用..

在您调用 services.AddSomething() 时,服务 provider 尚未从服务集合中构建。所以你不能在那个时候实例化一个服务。幸运的是,有一种方法可以在使用依赖注入的同时配置服务。

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

为此,您需要实施 IConfigureOptions<TOptions>(或者实际上 IConfigureNamedOptions<TOptions> 用于身份验证选项)并将其注册为单例。为了您的目的,这可能看起来像这样:

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 => { … }) 时发生的事情完全相同,只是被抽象掉了,因此您无需关心它。但是通过手动执行,您现在拥有更多的权力和访问完整的依赖注入服务提供商。