MicroElements.Swashbuckle.FluentValidation 6.0.0-beta.3

Prefix Reserved
This is a prerelease version of MicroElements.Swashbuckle.FluentValidation.
There is a newer version of this package available.
See the version list below for details.
dotnet add package MicroElements.Swashbuckle.FluentValidation --version 6.0.0-beta.3                
NuGet\Install-Package MicroElements.Swashbuckle.FluentValidation -Version 6.0.0-beta.3                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MicroElements.Swashbuckle.FluentValidation" Version="6.0.0-beta.3" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add MicroElements.Swashbuckle.FluentValidation --version 6.0.0-beta.3                
#r "nuget: MicroElements.Swashbuckle.FluentValidation, 6.0.0-beta.3"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install MicroElements.Swashbuckle.FluentValidation as a Cake Addin
#addin nuget:?package=MicroElements.Swashbuckle.FluentValidation&version=6.0.0-beta.3&prerelease

// Install MicroElements.Swashbuckle.FluentValidation as a Cake Tool
#tool nuget:?package=MicroElements.Swashbuckle.FluentValidation&version=6.0.0-beta.3&prerelease                

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

License NuGetVersion NuGetDownloads MyGetVersion

Build and publish AppVeyor Coverage Status

Gitter

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-beta.3" />
        <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

SwaggerSample

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

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (74)

Showing the top 5 NuGet packages that depend on MicroElements.Swashbuckle.FluentValidation:

Package Downloads
Reo.Core.Validation

Package Description

Reo.Core.Hosting

Package Description

Reo.Core.DistributedCache.Abstractions

Package Description

Reo.Core.Queue.RabbitMq

Package Description

Reo.Core.IdentityModel

Package Description

GitHub repositories (3)

Showing the top 3 popular GitHub repositories that depend on MicroElements.Swashbuckle.FluentValidation:

Repository Stars
AlphaYu/adnc
.NET微服务/分布式开发框架,同时也适用于单体架构系统的开发。
micro-elements/MicroElements.Swashbuckle.FluentValidation
Use FluentValidation rules instead of ComponentModel attributes
AntonioFalcaoJr/EventualShop
A state-of-the-art distributed system using Reactive DDD as uncertainty modeling, Event Storming as subdomain decomposition, Event Sourcing as an eventual persistence mechanism, CQRS, Async Projections, Microservices for individual deployable units, Event-driven Architecture for efficient integration, and Clean Architecture as domain-centric design
Version Downloads Last updated
6.0.0 2,741,014 12/3/2023
6.0.0-beta.3 242,642 2/19/2023
6.0.0-beta.1 229,831 7/23/2022
5.7.0 10,261,596 6/10/2022
5.6.0 1,102,360 5/3/2022
5.5.0 655,078 3/28/2022
5.4.0 3,143,786 9/19/2021
5.3.0 187,672 9/1/2021
5.2.0 444,919 7/31/2021
5.1.0 277,434 6/15/2021
5.1.0-rc.2 78,814 6/3/2021
5.1.0-rc.1 16,088 6/1/2021
5.0.0 59,461 5/30/2021
5.0.0-rc.2 9,113 5/18/2021
5.0.0-rc.1 2,529 5/16/2021
4.3.0 804,628 3/25/2021
4.3.0-rc.1 515 3/21/2021
4.2.0 809,163 2/18/2021
4.1.0 247,601 1/21/2021
4.1.0-rc.1 5,682 1/6/2021
4.0.0 1,609,074 8/21/2020
4.0.0-rc.2 20,869 7/18/2020
4.0.0-rc.1 66,962 6/15/2020
3.2.0 859,628 6/14/2020
3.1.1 546,179 4/28/2020
3.1.0 324,681 3/25/2020
3.0.0 165,788 3/7/2020
3.0.0-rc.6 86,560 2/5/2020
3.0.0-rc.5 269,407 1/24/2020
3.0.0-rc.4 115,979 12/29/2019
3.0.0-rc.3 90,560 11/28/2019
3.0.0-rc.2 47,795 10/13/2019
3.0.0-rc.1 11,509 9/30/2019
3.0.0-beta.1 34,462 4/26/2019
3.0.0-alpha.1 11,549 4/23/2019
2.3.0 73,708 11/14/2019
2.2.1 22,391 10/13/2019
2.2.0 62,039 8/28/2019
2.1.1 221,083 4/26/2019
2.1.0 95,490 4/8/2019
2.0.1 5,249 4/6/2019
2.0.0 30,860 3/13/2019
2.0.0-beta.5 27,154 2/13/2019
2.0.0-beta.4 3,244 2/11/2019
2.0.0-beta.3 3,968 1/28/2019
2.0.0-beta.2 7,525 1/10/2019
2.0.0-beta.1 13,549 11/12/2018
1.2.0 78,819 1/22/2019
1.1.0 32,611 11/10/2018
1.0.0 29,891 9/26/2018
1.0.0-rc.1 524 9/22/2018
1.0.0-beta.3 521 9/19/2018
1.0.0-beta.2 529 9/10/2018
1.0.0-beta.1 552 8/26/2018
0.8.2 15,400 6/29/2018
0.8.1 10,704 6/20/2018
0.8.0 2,251 6/12/2018
0.8.0-beta.1 751 5/11/2018
0.7.0 4,853 4/11/2018
0.6.0 2,276 4/4/2018
0.5.0 2,301 3/30/2018
0.4.0 2,473 3/29/2018
0.3.0 2,285 3/25/2018
0.2.0 2,198 3/25/2018
0.1.0 9,148 3/20/2018