micro-elements - MicroElements.Swashbuckle.FluentValidation 6.1.0
Swagger ISchemaFilter that uses FluentValidation validators instead System.ComponentModel based attributes.
PM> Install-Package MicroElements.Swashbuckle.FluentValidation -Version 6.1.0 -Source https://www.myget.org/F/micro-elements/api/v3/index.json
> nuget.exe install MicroElements.Swashbuckle.FluentValidation -Version 6.1.0 -Source https://www.myget.org/F/micro-elements/api/v3/index.json
> dotnet add package MicroElements.Swashbuckle.FluentValidation --version 6.1.0 --source https://www.myget.org/F/micro-elements/api/v3/index.json
<PackageReference Include="MicroElements.Swashbuckle.FluentValidation" Version="6.1.0" />
Copy to clipboard
source https://www.myget.org/F/micro-elements/api/v3/index.json
nuget MicroElements.Swashbuckle.FluentValidation ~> 6.1.0
Copy to clipboard
> choco install MicroElements.Swashbuckle.FluentValidation --version 6.1.0 --source https://www.myget.org/F/micro-elements/api/v2
Import-Module PowerShellGet
Register-PSRepository -Name "micro-elements" -SourceLocation "https://www.myget.org/F/micro-elements/api/v2"
Install-Module -Name "MicroElements.Swashbuckle.FluentValidation" -RequiredVersion "6.1.0" -Repository "micro-elements"
Copy to clipboard
MicroElements.Swashbuckle.FluentValidation
Use FluentValidation rules instead of ComponentModel attributes to define swagger schema.
Note: For WebApi see: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation.WebApi
Statuses
Supporting the project
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 via OpenCollective which will help keep the project going 🙏.
Usage
1. Minimal API
MinimalApi.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation.AspNetCore" Version="11.2.2" />
<PackageReference Include="MicroElements.Swashbuckle.FluentValidation" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
</Project>
Program.cs
using FluentValidation;
using FluentValidation.AspNetCore;
using MicroElements.Swashbuckle.FluentValidation.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
// Asp.Net stuff
services.AddControllers();
services.AddEndpointsApiExplorer();
// Add Swagger
services.AddSwaggerGen();
// Add FV
services.AddFluentValidationAutoValidation();
services.AddFluentValidationClientsideAdapters();
// Add FV validators
services.AddValidatorsFromAssemblyContaining<Program>();
// Add FV Rules to swagger
services.AddFluentValidationRulesToSwagger();
var app = builder.Build();
// Use Swagger
app.UseSwagger();
app.UseSwaggerUI();
app.MapControllers();
app.Run();
2. AspNetCore WebApi
Reference packages in your web project
<PackageReference Include="FluentValidation.AspNetCore" Version="11.1.0" />
<PackageReference Include="MicroElements.Swashbuckle.FluentValidation" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.0" />
Change Startup.cs
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Asp.net stuff
services.AddControllers();
// HttpContextValidatorRegistry requires access to HttpContext
services.AddHttpContextAccessor();
// Register FV validators
services.AddValidatorsFromAssemblyContaining<Startup>(lifetime: ServiceLifetime.Scoped);
// Add FV to Asp.net
services.AddFluentValidationAutoValidation();
// Add swagger
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { 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 logging
services.AddLogging(builder => builder.AddConsole());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
// Adds swagger
app.UseSwagger();
// Adds swagger UI
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
}
Version compatibility
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) |
Sample application
See sample project: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/tree/master/samples/SampleWebApi
Supported validators
- 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)
Extensibility
You can register FluentValidationRule in ServiceCollection.
User defined rule name replaces default rule with the same.
Full list of default rules can be get by FluentValidationRules.CreateDefaultRules()
List or default rules:
- Required
- NotEmpty
- Length
- Pattern
- Comparison
- Between
Example of rule:
new FluentValidationRule("Pattern")
{
Matches = propertyValidator => propertyValidator is IRegularExpressionValidator,
Apply = context =>
{
var regularExpressionValidator = (IRegularExpressionValidator)context.PropertyValidator;
context.Schema.Properties[context.PropertyKey].Pattern = regularExpressionValidator.Expression;
}
},
Samples
Swagger Sample model and validator
public class Sample
{
public string PropertyWithNoRules { get; set; }
public string NotNull { get; set; }
public string NotEmpty { get; set; }
public string EmailAddress { get; set; }
public string RegexField { get; set; }
public int ValueInRange { get; set; }
public int ValueInRangeExclusive { get; set; }
public float ValueInRangeFloat { get; set; }
public double ValueInRangeDouble { get; set; }
}
public class SampleValidator : AbstractValidator<Sample>
{
public SampleValidator()
{
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 numbers
RuleFor(sample => sample.ValueInRangeFloat).InclusiveBetween(1.1f, 5.3f);
RuleFor(sample => sample.ValueInRangeDouble).ExclusiveBetween(2.2, 7.5f);
}
}
Swagger Sample model screenshot
Validator with Include
public class CustomerValidator : AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(customer => customer.Surname).NotEmpty();
RuleFor(customer => customer.Forename).NotEmpty().WithMessage("Please specify a first name");
Include(new CustomerAddressValidator());
}
}
internal class CustomerAddressValidator : AbstractValidator<Customer>
{
public CustomerAddressValidator()
{
RuleFor(customer => customer.Address).Length(20, 250);
}
}
Get params bounded to validatable models
MicroElements.Swashbuckle.FluentValidation updates swagger schema for operation parameters bounded to validatable models.
Defining rules dynamically from database
See BlogValidator in sample.
Common problems and workarounds
Error: System.InvalidOperationException: 'Cannot resolve 'IValidator<T>' from root provider because it requires scoped service 'TDependency'
Workarounds in order or preference:
Workaround 1 (Use HttpContextServiceProviderValidatorFactory) by @WarpSpideR
public void ConfigureServices(IServiceCollection services)
{
// HttpContextServiceProviderValidatorFactory requires access to HttpContext
services.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);
});
Workaround 2 (Use ScopedSwaggerMiddleware)
Replace UseSwagger
for UseScopedSwagger
:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app
.UseMvc()
// Use scoped swagger if you have problems with scoped services in validators
.UseScopedSwagger();
Workaround 3 (Set ValidateScopes to false)
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
// Needed for using scoped services (for example DbContext) in validators
.UseDefaultServiceProvider(options => options.ValidateScopes = false)
.UseStartup<Startup>()
.Build();
Problem: I cant use several validators of one type
Example: You split validator into several small validators but AspNetCore uses only one of them.
Workaround: Hide dependent validators with internal
and use Include
to include other validation rules to one "Main" validator.
Problem: I'm using FluentValidation
or FluentValidation.DependencyInjectionExtensions
instead of FluentValidation.AspNetCore
If you are using the more basic FluentValidation
or FluentValidation.DependencyInjectionExtensions
libraries, then they will not automatically register IValidatorFactory
and you will get an error at runtime: "ValidatorFactory is not provided. Please register FluentValidation." In that case you must register it manually (see issue 97 for more details):
services.TryAddTransient<IValidatorFactory, ServiceProviderValidatorFactory>();
services.AddFluentValidationRulesToSwagger();
Problem: Newtonsoft.Json DefaultNamingStrategy, SnakeCaseNamingStrategy does not work
Startup.cs:
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = new NewtonsoftJsonNamingPolicy(new SnakeCaseNamingStrategy());
//options.JsonSerializerOptions.DictionaryKeyPolicy = new NewtonsoftJsonNamingPolicy(new SnakeCaseNamingStrategy());
})
/// <summary>
/// Allows use Newtonsoft <see cref="NamingStrategy"/> as System.Text <see cref="JsonNamingPolicy"/>.
/// </summary>
public class NewtonsoftJsonNamingPolicy : JsonNamingPolicy
{
private readonly NamingStrategy _namingStrategy;
/// <summary>
/// Creates new instance of <see cref="NewtonsoftJsonNamingPolicy"/>.
/// </summary>
/// <param name="namingStrategy">Newtonsoft naming strategy.</param>
public NewtonsoftJsonNamingPolicy(NamingStrategy namingStrategy)
{
_namingStrategy = namingStrategy;
}
/// <inheritdoc />
public override string ConvertName(string name)
{
return _namingStrategy.GetPropertyName(name, false);
}
}
Credits
Initial version of this project was based on Mujahid Daud Khan answer on StackOverflow: https://stackoverflow.com/questions/44638195/fluent-validation-with-swagger-in-asp-net-core/49477995#49477995
$# Changes in 6.1.0
- Added support for .NET 8 and .NET 9 to MicroElements.Swashbuckle.FluentValidation.AspNetCore
- Dropped support for .NET 6.0
- Updated NJsonSchema to version 10.6.10
Changes in 6.0.0
- see changelog for betas
Changes in 6.0.0 - beta.3:
- Added:
IFluentValidationRuleProvider
can be replaced with DI - Added:
ISchemaGenerationOptions.ValidatorSearch
IsOneValidatorForType
: bool; Valuetrue
: Gets only one validator (default),false
: Gets all suitable validators (new)SearchBaseTypeValidators
: allows to search base type validators
- Fixed: Stack Overflow Exception when using recursive validator type (PR#122 by @rachelpetitto)
- Deleted:
FluentValidationRulesRegistrator
- Deleted:
SwaggerGenOptions
from filters - Many minor code cleanups
Changes in 6.0.0 - beta.2:
- Codebase unified with NSwag
- Added: MicroElements.NSwag.FluentValidation package. Early version
- Change:
INameResolver
removed from FluentValidationRules ctor. Set it fromSchemaGenerationOptions
- Change:
ISchemaGenerationSettings
merged toISchemaGenerationOptions
- Change:
IValidatorRegistry
and it's implementations moved to MicroElements.OpenApi.FluentValidation namespace and package - Change:
IValidatorRegistry
can return more than one validator with methodGetValidators
- Added:
ValidatorSearch
strategy OneForType, ManyForType - Added:
ISchemaGenerationOptions.ValidatorFilter
,ISchemaGenerationOptions.RuleFilter
,ISchemaGenerationOptions.RuleComponentFilter
- Default Rule and RuleComponent filters checks that rule or component has no conditions.
- Default ValidatorFilter checks that validator CanValidateInstancesOfType
- Change:
UseAllOfForMultipleRules
typo fix
Changes in 6.0.0 - beta.1:
- Abstracted common logic for NSwag
- Moved from
IValidationFactory
(obsolete in FV 11.1.0) toIValidationRegistry
- Supported FluentValidation 11
AddFluentValidationAutoValidation
- Removed
HttpContextServiceProviderValidatorFactory
- Experimental
DocumentFilter
Full release notes can be found at: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
-
.NETStandard 2.0
- FluentValidation (>= 10.0.0 && < 12.0.0)
- MicroElements.OpenApi.FluentValidation (>= 6.1.0)
- Swashbuckle.AspNetCore.SwaggerGen (>= 6.3.0 && < 8.0.0)
- .NETStandard 2.0: 2.0.0.0
OwnersAlexey Petryashev |
Authorsalexey.petriashev, MicroElements |
Project URLhttps://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation |
LicenseMIT |
Tagsswagger swashbuckle FluentValidation aspnetcore |
Info546 total downloads |
2 downloads for version 6.1.0 |
Download (39.52 KB) |
Found on the current feed only |
Package history
Version | Size | Last updated | Downloads | Mirrored? | |||
---|---|---|---|---|---|---|---|
6.1.0 | 39.52 KB | Wed, 27 Nov 2024 22:07:42 GMT | 2 | ||||
6.0.0 | 38.37 KB | Sat, 23 Dec 2023 19:44:23 GMT | 8 | ||||
6.0.0-beta.3 | 38.37 KB | Mon, 01 May 2023 13:55:43 GMT | 7 | ||||
6.0.0-beta.2 | 38.7 KB | Sun, 12 Feb 2023 20:28:39 GMT | 6 | ||||
6.0.0-beta.1 | 39.05 KB | Sat, 23 Jul 2022 20:03:41 GMT | 7 | ||||
5.7.0 | 43.32 KB | Fri, 10 Jun 2022 10:38:09 GMT | 6 | ||||
5.6.0 | 43.32 KB | Fri, 10 Jun 2022 10:24:11 GMT | 6 | ||||
5.5.0 | 43.38 KB | Sun, 01 May 2022 19:40:08 GMT | 7 | ||||
5.4.0 | 43.58 KB | Mon, 28 Mar 2022 18:35:16 GMT | 8 | ||||
5.3.0 | 43.57 KB | Sun, 19 Sep 2021 15:22:18 GMT | 7 | ||||
5.2.0 | 43.48 KB | Sat, 31 Jul 2021 14:50:34 GMT | 8 | ||||
5.1.0 | 43.46 KB | Thu, 17 Jun 2021 15:59:54 GMT | 7 | ||||
5.1.0-rc.2 | 43.47 KB | Thu, 03 Jun 2021 20:35:12 GMT | 9 | ||||
5.1.0-rc.1 | 42.49 KB | Tue, 01 Jun 2021 08:14:50 GMT | 7 | ||||
5.0.0 | 41.65 KB | Sun, 30 May 2021 09:25:09 GMT | 7 | ||||
5.0.0-rc.2 | 41.67 KB | Tue, 18 May 2021 14:25:52 GMT | 7 | ||||
5.0.0-rc.1 | 41.64 KB | Tue, 18 May 2021 10:35:39 GMT | 7 | ||||
4.3.0 | 28.73 KB | Sun, 02 May 2021 18:37:25 GMT | 7 | ||||
4.3.0-rc.1 | 29.02 KB | Thu, 25 Mar 2021 16:20:24 GMT | 8 | ||||
4.2.0 | 28.92 KB | Sun, 21 Mar 2021 14:21:00 GMT | 9 | ||||
4.1.0 | 26.27 KB | Thu, 18 Feb 2021 19:18:07 GMT | 7 | ||||
4.1.0-rc.1 | 26.28 KB | Fri, 08 Jan 2021 14:04:28 GMT | 7 | ||||
4.0.0 | 23.81 KB | Fri, 01 Jan 2021 18:15:58 GMT | 7 | ||||
4.0.0-rc.2 | 22.58 KB | Sat, 18 Jul 2020 11:34:07 GMT | 8 | ||||
4.0.0-rc.1 | 22.91 KB | Mon, 15 Jun 2020 07:44:40 GMT | 7 | ||||
3.2.0 | 22.43 KB | Thu, 16 Jul 2020 08:55:46 GMT | 6 | ||||
3.1.1 | 21.06 KB | Tue, 28 Apr 2020 19:50:38 GMT | 6 | ||||
3.1.0 | 21.08 KB | Tue, 28 Apr 2020 19:44:12 GMT | 6 | ||||
3.0.0 | 21 KB | Fri, 06 Mar 2020 18:33:53 GMT | 9 | ||||
3.0.0-rc.6 | 20.78 KB | Wed, 05 Feb 2020 19:14:35 GMT | 9 | ||||
3.0.0-rc.5 | 20.83 KB | Fri, 24 Jan 2020 16:14:36 GMT | 10 | ||||
3.0.0-rc.4 | 19.93 KB | Sun, 29 Dec 2019 21:20:26 GMT | 9 | ||||
3.0.0-rc.3 | 18.63 KB | Thu, 28 Nov 2019 20:13:09 GMT | 10 | ||||
3.0.0-rc.2 | 18.46 KB | Sun, 13 Oct 2019 19:01:50 GMT | 8 | ||||
3.0.0-rc.1 | 18.29 KB | Mon, 30 Sep 2019 21:32:44 GMT | 8 | ||||
3.0.0-beta.1 | 18.08 KB | Thu, 25 Apr 2019 21:47:21 GMT | 10 | ||||
3.0.0-alpha.1 | 17.97 KB | Tue, 23 Apr 2019 20:36:02 GMT | 8 | ||||
2.3.0 | 18.92 KB | Thu, 14 Nov 2019 20:19:50 GMT | 9 | ||||
2.2.1 | 18.67 KB | Thu, 14 Nov 2019 19:23:42 GMT | 8 | ||||
2.2.0 | 18.3 KB | Wed, 28 Aug 2019 20:28:53 GMT | 8 | ||||
2.1.1 | 17.83 KB | Thu, 25 Apr 2019 21:48:11 GMT | 14 | ||||
2.1.0 | 17.73 KB | Mon, 08 Apr 2019 12:24:34 GMT | 9 | ||||
2.0.1 | 17.79 KB | Mon, 08 Apr 2019 12:06:38 GMT | 8 | ||||
2.0.0 | 16.5 KB | Wed, 13 Mar 2019 19:08:14 GMT | 8 | ||||
2.0.0-beta.5 | 16.29 KB | Sun, 24 Feb 2019 07:31:07 GMT | 8 | ||||
2.0.0-beta.4 | 16.31 KB | Wed, 13 Feb 2019 20:13:52 GMT | 9 | ||||
2.0.0-beta.3 | 16.36 KB | Mon, 11 Feb 2019 20:23:30 GMT | 8 | ||||
2.0.0-beta.2 | 16.81 KB | Mon, 21 Jan 2019 20:37:24 GMT | 8 | ||||
2.0.0-beta.1 | 16.76 KB | Mon, 12 Nov 2018 20:40:04 GMT | 8 | ||||
1.2.0 | 16.09 KB | Tue, 22 Jan 2019 07:33:31 GMT | 8 | ||||
1.1.0 | 16.1 KB | Mon, 21 Jan 2019 22:00:43 GMT | 7 | ||||
1.0.0 | 16.5 KB | Wed, 26 Sep 2018 20:08:53 GMT | 9 | ||||
1.0.0-rc.1 | 16.52 KB | Fri, 21 Sep 2018 20:37:01 GMT | 7 | ||||
1.0.0-beta.3 | 16.5 KB | Wed, 19 Sep 2018 19:48:11 GMT | 9 | ||||
1.0.0-beta.2 | 15.93 KB | Mon, 10 Sep 2018 16:27:12 GMT | 8 | ||||
1.0.0-beta.1 | 14.27 KB | Fri, 24 Aug 2018 17:32:30 GMT | 8 | ||||
0.8.2 | 14.05 KB | Fri, 29 Jun 2018 09:29:45 GMT | 8 | ||||
0.8.1 | 14.02 KB | Wed, 20 Jun 2018 23:33:00 GMT | 9 | ||||
0.8.0 | 13.96 KB | Tue, 12 Jun 2018 22:26:34 GMT | 10 | ||||
0.8.0-beta.3 | 13.97 KB | Tue, 12 Jun 2018 22:21:17 GMT | 11 | ||||
0.8.0-beta.2 | 13.83 KB | Mon, 11 Jun 2018 10:57:57 GMT | 8 | ||||
0.8.0-beta.1 | 13.24 KB | Fri, 11 May 2018 10:39:26 GMT | 10 | ||||
0.7.0 | 13.1 KB | Fri, 11 May 2018 10:24:33 GMT | 6 | ||||
0.6.0 | 10.27 KB | Wed, 04 Apr 2018 19:32:20 GMT | 9 | ||||
0.5.0 | 10.24 KB | Fri, 30 Mar 2018 20:04:37 GMT | 7 | ||||
0.4.0 | 9.81 KB | Thu, 29 Mar 2018 23:07:36 GMT | 10 | ||||
0.3.0 | 8.46 KB | Thu, 29 Mar 2018 21:28:37 GMT | 7 | ||||
0.2.0 | 6.73 KB | Sun, 25 Mar 2018 15:14:15 GMT | 7 | ||||
0.0.2 | 6.73 KB | Fri, 23 Mar 2018 20:19:30 GMT | 8 |