I am using FastEndpoint to perfom some validations in requests, so, I need to obtain a instance of IValidator<T>
in order to get its properties using reflection but it returns null for some reason
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
}
}
Startup.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";
});
}
}
I read this document about dependency injection in FastEndpoint validators but it doesn't contain information for my user case
I found a more holistic solution which is using
IServiceCollection
extension methodAddValidatorsFromAssemblyContaining<T>()
of nuget packageFluentValidation.DependencyInjectionExtensions
where It adds all validators in the assembly of the type specified by the generic parameter.