Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork69
Use FluentValidation rules instead of ComponentModel attributes
License
micro-elements/MicroElements.Swashbuckle.FluentValidation
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Use FluentValidation rules instead of ComponentModel attributes to define swagger schema.
Note: For WebApi see:https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation.WebApi
MicroElements.Swashbuckle.FluentValidation is developed and supported by@petriashev for free in his spare time.If you find MicroElements.Swashbuckle.FluentValidation useful, please consider financially supporting the project viaOpenCollective which will help keep the project going 🙏.
<ProjectSdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> </PropertyGroup> <ItemGroup> <PackageReferenceInclude="FluentValidation.AspNetCore"Version="11.2.2" /> <PackageReferenceInclude="MicroElements.Swashbuckle.FluentValidation"Version="6.0.0" /> <PackageReferenceInclude="Microsoft.AspNetCore.OpenApi"Version="7.0.2" /> <PackageReferenceInclude="Swashbuckle.AspNetCore"Version="6.4.0" /> </ItemGroup> </Project>
usingFluentValidation;usingFluentValidation.AspNetCore;usingMicroElements.Swashbuckle.FluentValidation.AspNetCore;varbuilder=WebApplication.CreateBuilder(args);varservices=builder.Services;// Asp.Net stuffservices.AddControllers();services.AddEndpointsApiExplorer();// Add Swaggerservices.AddSwaggerGen();// Add FVservices.AddFluentValidationAutoValidation();services.AddFluentValidationClientsideAdapters();// Add FV validatorsservices.AddValidatorsFromAssemblyContaining<Program>();// Add FV Rules to swaggerservices.AddFluentValidationRulesToSwagger();varapp=builder.Build();// Use Swaggerapp.UseSwagger();app.UseSwaggerUI();app.MapControllers();app.Run();
<PackageReferenceInclude="FluentValidation.AspNetCore"Version="11.1.0" /><PackageReferenceInclude="MicroElements.Swashbuckle.FluentValidation"Version="6.0.0" /><PackageReferenceInclude="Swashbuckle.AspNetCore"Version="6.3.0" />
// This method gets called by the runtime. Use this method to add services to the container.publicvoidConfigureServices(IServiceCollectionservices){// Asp.net stuffservices.AddControllers();// HttpContextValidatorRegistry requires access to HttpContextservices.AddHttpContextAccessor();// Register FV validatorsservices.AddValidatorsFromAssemblyContaining<Startup>(lifetime:ServiceLifetime.Scoped);// Add FV to Asp.netservices.AddFluentValidationAutoValidation();// Add swaggerservices.AddSwaggerGen(c=>{c.SwaggerDoc("v1",newOpenApiInfo{Title="My API",Version="v1"});});// [Optional] Add INameResolver (SystemTextJsonNameResolver will be registered by default)// services.AddSingleton<INameResolver, CustomNameResolver>();// Adds FluentValidationRules staff to Swagger. (Minimal configuration)services.AddFluentValidationRulesToSwagger();// [Optional] Configure generation options for your needs. Also can be done with services.Configure<SchemaGenerationOptions>// services.AddFluentValidationRulesToSwagger(options =>// {// options.SetNotNullableIfMinLengthGreaterThenZero = true;// options.UseAllOffForMultipleRules = true;// });// Adds loggingservices.AddLogging(builder=>builder.AddConsole());}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.publicvoidConfigure(IApplicationBuilderapp,IHostingEnvironmentenv){app.UseRouting();app.UseEndpoints(endpoints=>{endpoints.MapControllers();});// Adds swaggerapp.UseSwagger();// Adds swagger UIapp.UseSwaggerUI(c=>{c.SwaggerEndpoint("/swagger/v1/swagger.json","My API V1");});}
MicroElements.Swashbuckle.FluentValidation | Swashbuckle.AspNetCore | FluentValidation |
---|---|---|
[1.1.0, 2.0.0) | [3.0.0, 4.0.0) | >=7.2.0 |
[2.0.0, 3.0.0) | [4.0.0, 5.0.0) | >=8.1.3 |
[3.0.0, 3.1.0) | [5.0.0, 5.2.0) | >=8.3.0 |
[3.1.0, 4.2.1) | [5.2.0, 6.0.0) | >=8.3.0 |
[4.2.0, 5.0.0) | [5.5.1, 7.0.0) | [9.0.0, 10) |
[5.0.0, 6.0.0) | [6.3.0, 7.0.0) | [10.0.0, 12) |
See sample project:https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/tree/master/samples/SampleWebApi
- INotNullValidator (NotNull)
- INotEmptyValidator (NotEmpty)
- ILengthValidator (for strings: Length, MinimumLength, MaximumLength, ExactLength) (for arrays: MinItems, MaxItems)
- IRegularExpressionValidator (Email, Matches)
- IComparisonValidator (GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual)
- IBetweenValidator (InclusiveBetween, ExclusiveBetween)
You can register FluentValidationRule in ServiceCollection.
User defined rule name replaces default rule with the same.Full list of default rules can be get byFluentValidationRules.CreateDefaultRules()
List or default rules:
- Required
- NotEmpty
- Length
- Pattern
- Comparison
- Between
Example of rule:
newFluentValidationRule("Pattern"){Matches= propertyValidator=>propertyValidatorisIRegularExpressionValidator,Apply= context=>{varregularExpressionValidator=(IRegularExpressionValidator)context.PropertyValidator;context.Schema.Properties[context.PropertyKey].Pattern=regularExpressionValidator.Expression;}},
publicclassSample{publicstringPropertyWithNoRules{get;set;}publicstringNotNull{get;set;}publicstringNotEmpty{get;set;}publicstringEmailAddress{get;set;}publicstringRegexField{get;set;}publicintValueInRange{get;set;}publicintValueInRangeExclusive{get;set;}publicfloatValueInRangeFloat{get;set;}publicdoubleValueInRangeDouble{get;set;}}publicclassSampleValidator:AbstractValidator<Sample>{publicSampleValidator(){RuleFor(sample=>sample.NotNull).NotNull();RuleFor(sample=>sample.NotEmpty).NotEmpty();RuleFor(sample=>sample.EmailAddress).EmailAddress();RuleFor(sample=>sample.RegexField).Matches(@"(\d{4})-(\d{2})-(\d{2})");RuleFor(sample=>sample.ValueInRange).GreaterThanOrEqualTo(5).LessThanOrEqualTo(10);RuleFor(sample=>sample.ValueInRangeExclusive).GreaterThan(5).LessThan(10);// WARNING: Swashbuckle implements minimum and maximim as int so you will loss fraction part of float and double numbersRuleFor(sample=>sample.ValueInRangeFloat).InclusiveBetween(1.1f,5.3f);RuleFor(sample=>sample.ValueInRangeDouble).ExclusiveBetween(2.2,7.5f);}}
publicclassCustomerValidator:AbstractValidator<Customer>{publicCustomerValidator(){RuleFor(customer=>customer.Surname).NotEmpty();RuleFor(customer=>customer.Forename).NotEmpty().WithMessage("Please specify a first name");Include(newCustomerAddressValidator());}}internalclassCustomerAddressValidator:AbstractValidator<Customer>{publicCustomerAddressValidator(){RuleFor(customer=>customer.Address).Length(20,250);}}
MicroElements.Swashbuckle.FluentValidation updates swagger schema for operation parameters bounded to validatable models.
See BlogValidator in sample.
Error:System.InvalidOperationException: 'Cannot resolve 'IValidator<T>' from root provider because it requires scoped service 'TDependency'
Workarounds in order or preference:
publicvoidConfigureServices(IServiceCollectionservices){// HttpContextServiceProviderValidatorFactory requires access to HttpContextservices.AddHttpContextAccessor();services.AddMvc()// Adds fluent validators to Asp.net.AddFluentValidation(c=>{c.RegisterValidatorsFromAssemblyContaining<Startup>();// Optionally set validator factory if you have problems with scope resolve inside validators.c.ValidatorFactoryType=typeof(HttpContextServiceProviderValidatorFactory);});
ReplaceUseSwagger
forUseScopedSwagger
:
publicvoidConfigure(IApplicationBuilderapp,IHostingEnvironmentenv){app.UseMvc()// Use scoped swagger if you have problems with scoped services in validators.UseScopedSwagger();
publicstaticIWebHostBuildWebHost(string[]args)=>WebHost.CreateDefaultBuilder(args)// Needed for using scoped services (for example DbContext) in validators.UseDefaultServiceProvider(options=>options.ValidateScopes=false).UseStartup<Startup>().Build();
Example: You split validator into several small validators but AspNetCore uses only one of them.
Workaround: Hide dependent validators withinternal
and useInclude
to include other validation rules to one "Main" validator.
Problem: I'm usingFluentValidation
orFluentValidation.DependencyInjectionExtensions
instead ofFluentValidation.AspNetCore
If you are using the more basicFluentValidation
orFluentValidation.DependencyInjectionExtensions
libraries, then they will not automatically registerIValidatorFactory
and you will get an error at runtime: "ValidatorFactory is not provided. Please register FluentValidation." In that case you must register it manually (seeissue 97 for more details):
services.TryAddTransient<IValidatorFactory,ServiceProviderValidatorFactory>();services.AddFluentValidationRulesToSwagger();
Startup.cs:.AddJsonOptions(options=>{options.JsonSerializerOptions.PropertyNamingPolicy=newNewtonsoftJsonNamingPolicy(newSnakeCaseNamingStrategy());//options.JsonSerializerOptions.DictionaryKeyPolicy = new NewtonsoftJsonNamingPolicy(new SnakeCaseNamingStrategy());})/// <summary>/// Allows use Newtonsoft <see cref="NamingStrategy"/> as System.Text <see cref="JsonNamingPolicy"/>./// </summary>publicclassNewtonsoftJsonNamingPolicy:JsonNamingPolicy{privatereadonlyNamingStrategy_namingStrategy;/// <summary>/// Creates new instance of <see cref="NewtonsoftJsonNamingPolicy"/>./// </summary>/// <param name="namingStrategy">Newtonsoft naming strategy.</param>publicNewtonsoftJsonNamingPolicy(NamingStrategynamingStrategy){_namingStrategy=namingStrategy;}/// <inheritdoc />publicoverridestringConvertName(stringname){return_namingStrategy.GetPropertyName(name,false);}}
Initial version of this project was based onMujahid Daud Khan answer on StackOverflow:https://stackoverflow.com/questions/44638195/fluent-validation-with-swagger-in-asp-net-core/49477995#49477995
About
Use FluentValidation rules instead of ComponentModel attributes
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.