无法从ISchemaFilter中的ServiceProvider获取FastEndpoint验证器实例的IValidator<T>

问题描述 投票:0回答:1

我正在使用 FastEndpoint 在请求中执行一些验证,因此,我需要获取

IValidator<T>
的实例,以便使用反射获取其属性,但由于某种原因它返回 null

SwaggerFluentValidationRules .cs

using FluentValidation;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using PakEnergy.Services.SharedKernel.Request;
using PakEnergy.Services.SharedKernel.Validator;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace PakEnergy.Services.SharedKernel.Swagger;

public class SwaggerFluentValidationRules : ISchemaFilter
{
    private readonly IServiceProvider _serviceProvider;

    public SwaggerFluentValidationRules(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void Apply(OpenApiSchema model, SchemaFilterContext context)
    {
        
        var genericType = typeof(IValidator<>).MakeGenericType(context.Type);
        // validator is always null
        var validator = _serviceProvider.GetService(genericType) as IValidator; 

        // More logic here
    }
}

启动.cs

namespace PakEnergy.Services.CompanyService.Api;

[ExcludeFromCodeCoverage]
public abstract class Startup
{
    const string CorsPolicyName = "CorsPolicy";

    public static void Run(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
        builder.Host.UseSerilog((ctx, config) =>
            {
                var configuration = new ConfigurationBuilder().AddConfiguration(ctx.Configuration).Build();
                config.ReadFrom.Configuration(configuration);
            })
            .ConfigureContainer<ContainerBuilder>(containerBuilder =>
            {
                containerBuilder.RegisterModule(new DefaultCoreModule());
                containerBuilder.RegisterModule(new DefaultInfrastructureModule());
                containerBuilder.RegisterModule(new SharedPolicyHandlersModule());
            });

        // Extracted into it's own method
        ConfigureServices(builder);

        var app = builder.Build();
        app.UseSerilogRequestLogging();
        app.UseDefaultExceptionHandler(logStructuredException: true);

        if (app.Environment.IsEnvironment("PreProduction") || app.Environment.IsProduction())
        {
            app.UseHttpsRedirection();
        }

        if (!app.Environment.IsProduction())
        {
            app.UseSwagger();
            app.UseSwaggerUI(
                c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "PakEnergy.Services.CompanyService.Api V1"); });
        }

        app.UseCors(CorsPolicyName);
        app.UseRouting();
        app.UseFastEndpoints(c =>
        {
            c.Endpoints.RoutePrefix = "api";
            c.Endpoints.Configurator = ep =>
            {
                ep.PreProcessors(Order.Before, new IfMatchHeaderValidator<Company>());
                ep.ResponseInterceptor(new ETagResponseInterceptor());
            };

            c.Binding.ValueParserFor<ProductIdEnum>(ProductIdEnumParser.Parse);
        });

        app.UseAuthorization();

        using var scope = app.Services.CreateScope();
        var services = scope.ServiceProvider;

        var context = services.GetRequiredService<AppDbContext>();
        context.Database.Migrate();

        if (app.Environment.IsDevelopment() || app.Environment.IsEnvironment("QA"))
            app.UseDbSeeding(context);

        var separator = new string('#', 51);
        Console.WriteLine(separator);
        Console.WriteLine(
            $"### Starting PakEnergy.Services.CompanyService.Api in {builder.Environment.EnvironmentName}\t\t###");
        Console.WriteLine(separator);

        app.Run();
    }

    private static void ConfigureServices(WebApplicationBuilder builder)
    {
        var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
        
        var services = builder.Services;
        builder.Services.AddOptions();
        builder.Services.Configure<ConnectionStringSettings>(builder.Configuration.GetSection("ConnectionStrings"));
        services.AddSingleton<IConnectionStringSettings>(provider =>
            provider.GetRequiredService<IOptions<ConnectionStringSettings>>().Value);
        builder.Services.Configure<ProvisioningSettings>(builder.Configuration.GetSection("Provisioning"));
        services.AddSingleton<IProvisioningSettings>(provider =>
            provider.GetRequiredService<IOptions<ProvisioningSettings>>().Value);

        services.AddDbContext<AppDbContext>((sp, optionsBuilder) =>
        {
            var auditInterceptor = sp.GetRequiredService<EntityBaseAuditInterceptor>();
            var softDeleteInterceptor = sp.GetRequiredService<EntityBaseSoftDeleteInterceptor>();
            optionsBuilder.UseSqlServer(connectionString)
                .AddInterceptors(auditInterceptor, softDeleteInterceptor);
        });
        
        services.AddHttpContextAccessor();
        services.AddAutoMapper(typeof(CreateCompanyMapper));
        services.AddCors(options =>
        {
            options.AddPolicy(CorsPolicyName, policyBuilder =>
            {
                policyBuilder
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials()
                    .WithExposedHeaders("content-disposition", "etag")
                    .SetIsOriginAllowed(_ => true); // Allow any origin
            });
        });

        services.AddFastEndpoints();
        services.AddFastEndpointsApiExplorer();
        services.AddScoped<ISqlRunnerService, SqlRunnerService>();
        services.AddScoped<IDatabaseProvisioningService, DatabaseProvisioningService>();
        services.AddScoped<IStorageContainerProvisioningService, StorageContainerProvisioningService>();
        services.AddScoped<IProvisionStatusService, ProvisionStatusService>();
        services.AddScoped<ICompanyAccessService, CompanyAccessService>();
        services.AddScoped<ICompanyValidationService, CompanyValidationService>();
        services.AddScoped<ITaxValidationService, TaxValidationService>();
        services.AddScoped<ICompanyRepository, CompanyRepository>();
        services.AddScoped<IResourceNameGeneratorService, ResourceNameGeneratorService>();
        services.AddScoped<IBlobServiceClientFactory, BlobServiceClientFactory>();

        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1",
                new OpenApiInfo { Title = "PakEnergy.Services.CompanyService.Domain.Api", Version = "v1" });
            c.EnableAnnotations();
            c.OperationFilter<FastEndpointsOperationFilter>();
            c.OperationFilter<SwaggerOperationNotRequiredParametersFilter>();
            c.DocumentFilter<SwaggerEnumDocumentFilter>();
            c.SchemaFilter<SwaggerFluentValidationRules>();
            // Include 'SecurityScheme' to use JWT Authentication
            var jwtSecurityScheme = new OpenApiSecurityScheme
            {
                BearerFormat = "JWT",
                Name = "JWT Authentication",
                In = ParameterLocation.Header,
                Type = SecuritySchemeType.Http,
                Scheme = JwtBearerDefaults.AuthenticationScheme,
                Description = "Put **_ONLY_** your JWT Bearer token on textbox below!",

                Reference = new OpenApiReference
                {
                    Id = JwtBearerDefaults.AuthenticationScheme,
                    Type = ReferenceType.SecurityScheme
                }
            };

            c.AddSecurityDefinition("Bearer", jwtSecurityScheme);

            c.AddSecurityRequirement(new OpenApiSecurityRequirement()
            {
                { jwtSecurityScheme, new List<string>() }
            });
        });

        services
            .AddAuthentication()
            .AddJwtBearer(options =>
            {
                options.Authority = builder.Configuration.GetValue<string>("Auth0:Authority");
                options.Audience = builder.Configuration.GetValue<string>("Auth0:Audience");
            });

        builder.Services.AddAuthorization(options =>
        {
            options.AddPolicy(GlobalPolicies.RequiresMatchingProductId, policy =>
            {
                policy.AddRequirements(new RequiresMatchingProductIdRequirement());
            });
            options.AddPolicy(GlobalPolicies.RequiresMatchingCompanyId, policy =>
            {
                policy.AddRequirements(new RequiresMatchingCompanyIdRequirement());
            });
        });

        // add list services for diagnostic purposes - see https://github.com/ardalis/AspNetCoreStartupServices
        services.Configure<ServiceConfig>(config =>
        {
            config.Services = new List<ServiceDescriptor>(services);
            config.Path = "listservices";
        });
    }
}

我阅读了有关 FastEndpoint 验证器中的依赖注入的文档,但它不包含我的用户案例的信息

fluentvalidation swashbuckle swagger-codegen swashbuckle.aspnetcore fast-endpoints
1个回答
0
投票

我不确定你为什么使用 swashbuckle,因为 fastendpoints 只支持 nswag 来生成 swagger。 nswag 的等价物是

ISchemaProcessor
。 FE 也不在 DI 容器本身中注册验证器。因此,如果您需要在模式处理器中获取验证器实例,您必须自己在 DI 中注册验证器。这是一个如何实现这一目标的示例:

var bld = WebApplication.CreateBuilder(args);
bld.Services
   .AddFastEndpoints()
   .AddSingleton<IValidator<MyRequest>, MyValidator>() //register the validator yourself
   .SwaggerDocument(
       o =>
       {
           o.DocumentSettings = s =>
           {
               s.SchemaProcessors.Add(new MySchemaProcessor(bld.Services)); //pass down the service collection to the schema processor
           };
       });

var app = bld.Build();
app.UseFastEndpoints();
app.UseSwaggerGen();
app.Run();

class MySchemaProcessor : ISchemaProcessor
{
    readonly IServiceProvider _serviceProvider;

    public MySchemaProcessor(IServiceCollection serviceCollection)
    {
        _serviceProvider = serviceCollection.BuildServiceProvider();
    }

    public void Process(SchemaProcessorContext ctx)
    {
        var validator = _serviceProvider.GetService(typeof(IValidator<>).MakeGenericType(ctx.ContextualType.Type));

        if (validator is not null)
        {
            // do whatever you want here
        }
    }
}

sealed class MyRequest
{
    public int Id { get; set; }
}

sealed class MyValidator : Validator<MyRequest>
{
    public MyValidator()
    {
        RuleFor(r => r.Id).NotEmpty();
    }
}

sealed class MyEndpoint : Endpoint<MyRequest>
{
    public override void Configure()
    {
        Post("test");
        AllowAnonymous();
    }

    public override async Task HandleAsync(MyRequest r, CancellationToken c)
    {
        await SendAsync(r);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.