Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation

Use FluentValidation rules instead of ComponentModel attributes
https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation

Last synced: about 2 months ago
JSON representation

Use FluentValidation rules instead of ComponentModel attributes

Awesome Lists containing this project

README

        

# 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](https://img.shields.io/github/license/micro-elements/MicroElements.Swashbuckle.FluentValidation.svg)](https://raw.githubusercontent.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/master/LICENSE)
[![NuGetVersion](https://img.shields.io/nuget/v/MicroElements.Swashbuckle.FluentValidation.svg)](https://www.nuget.org/packages/MicroElements.Swashbuckle.FluentValidation)
![NuGetDownloads](https://img.shields.io/nuget/dt/MicroElements.Swashbuckle.FluentValidation.svg)
[![MyGetVersion](https://img.shields.io/myget/micro-elements/v/MicroElements.Swashbuckle.FluentValidation.svg)](https://www.myget.org/feed/micro-elements/package/nuget/MicroElements.Swashbuckle.FluentValidation)

![Build and publish](https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/workflows/Build%20and%20publish/badge.svg)
[![AppVeyor](https://img.shields.io/appveyor/ci/micro-elements/microelements-swashbuckle-fluentvalidation.svg?logo=appveyor)](https://ci.appveyor.com/project/micro-elements/microelements-swashbuckle-fluentvalidation)
[![Coverage Status](https://img.shields.io/coveralls/micro-elements/MicroElements.Swashbuckle.FluentValidation.svg)](https://coveralls.io/r/micro-elements/MicroElements.Swashbuckle.FluentValidation)

[![Gitter](https://img.shields.io/gitter/room/micro-elements/MicroElements.Swashbuckle.FluentValidation.svg)](https://gitter.im/micro-elements/MicroElements.Swashbuckle.FluentValidation)

### Supporting the project
MicroElements.Swashbuckle.FluentValidation is developed and supported by [@petriashev](https://github.com/petriashev) for free in his spare time.
If you find MicroElements.Swashbuckle.FluentValidation useful, please consider financially supporting the project via [OpenCollective](https://opencollective.com/micro-elements) which will help keep the project going 🙏.

## Usage

### 1. Minimal API

#### MinimalApi.csproj

```xml


net7.0
enable
enable







```

#### Program.cs

```csharp
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();

// 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

```xml

```

#### Change Startup.cs

```csharp
// 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(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();

// Adds FluentValidationRules staff to Swagger. (Minimal configuration)
services.AddFluentValidationRulesToSwagger();

// [Optional] Configure generation options for your needs. Also can be done with services.Configure
// 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:

```csharp
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

```csharp
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
{
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](image/swagger_sample.png "SwaggerSample")

### Validator with Include

```csharp
public class CustomerValidator : AbstractValidator
{
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
{
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' from root provider because it requires scoped service 'TDependency'`

Workarounds in order or preference:

#### Workaround 1 (Use HttpContextServiceProviderValidatorFactory) by @WarpSpideR

```csharp
public void ConfigureServices(IServiceCollection services)
{
// HttpContextServiceProviderValidatorFactory requires access to HttpContext
services.AddHttpContextAccessor();

services
.AddMvc()
// Adds fluent validators to Asp.net
.AddFluentValidation(c =>
{
c.RegisterValidatorsFromAssemblyContaining();
// 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`:

```csharp
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)

```csharp
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()
.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](https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/issues/97) for more details):
````cs
services.TryAddTransient();
services.AddFluentValidationRulesToSwagger();
````

## Problem: Newtonsoft.Json DefaultNamingStrategy, SnakeCaseNamingStrategy does not work

```csharp

Startup.cs:

.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = new NewtonsoftJsonNamingPolicy(new SnakeCaseNamingStrategy());
//options.JsonSerializerOptions.DictionaryKeyPolicy = new NewtonsoftJsonNamingPolicy(new SnakeCaseNamingStrategy());
})

///
/// Allows use Newtonsoft as System.Text .
///
public class NewtonsoftJsonNamingPolicy : JsonNamingPolicy
{
private readonly NamingStrategy _namingStrategy;

///
/// Creates new instance of .
///
/// Newtonsoft naming strategy.
public NewtonsoftJsonNamingPolicy(NamingStrategy namingStrategy)
{
_namingStrategy = namingStrategy;
}

///
public override string ConvertName(string name)
{
return _namingStrategy.GetPropertyName(name, false);
}
}
```

## Credits

Initial version of this project was based on
[Mujahid Daud Khan](https://stackoverflow.com/users/1735196/mujahid-daud-khan) answer on StackOverflow:
https://stackoverflow.com/questions/44638195/fluent-validation-with-swagger-in-asp-net-core/49477995#49477995