diff --git a/FluentValidation.AutoValidation.Endpoints/FluentValidation.AutoValidation.Endpoints.csproj b/FluentValidation.AutoValidation.Endpoints/FluentValidation.AutoValidation.Endpoints.csproj
index a98226e..b7e579f 100644
--- a/FluentValidation.AutoValidation.Endpoints/FluentValidation.AutoValidation.Endpoints.csproj
+++ b/FluentValidation.AutoValidation.Endpoints/FluentValidation.AutoValidation.Endpoints.csproj
@@ -10,7 +10,7 @@
SharpGrip.FluentValidation.AutoValidation.Endpoints
SharpGrip FluentValidation AutoValidation Endpoints
SharpGrip FluentValidation AutoValidation Endpoints is an extension of the FluentValidation library enabling automatic asynchronous validation in minimal APIs (endpoints).
- sharpgrip;validation;fluent-validation;minimal-api
+ sharpgrip;validation;fluent-validation;endpoints;minimal-api
diff --git a/FluentValidation.AutoValidation.Endpoints/src/Configuration/AutoValidationEndpointsConfiguration.cs b/FluentValidation.AutoValidation.Endpoints/src/Configuration/AutoValidationEndpointsConfiguration.cs
new file mode 100644
index 0000000..4e82152
--- /dev/null
+++ b/FluentValidation.AutoValidation.Endpoints/src/Configuration/AutoValidationEndpointsConfiguration.cs
@@ -0,0 +1,25 @@
+using System;
+using Microsoft.AspNetCore.Http.HttpResults;
+using SharpGrip.FluentValidation.AutoValidation.Endpoints.Results;
+
+namespace SharpGrip.FluentValidation.AutoValidation.Endpoints.Configuration
+{
+ public class AutoValidationEndpointsConfiguration
+ {
+ ///
+ /// Holds the overridden result factory. This property is meant for infrastructure and should not be used by application code.
+ ///
+ public Type? OverriddenResultFactory { get; private set; }
+
+ ///
+ /// Overrides the default result factory with a custom result factory. Custom result factories are required to implement .
+ /// The default result factory returns the validation errors wrapped in a object.
+ ///
+ ///
+ /// The custom result factory implementing .
+ public void OverrideDefaultResultFactoryWith() where TResultFactory : IFluentValidationAutoValidationResultFactory
+ {
+ OverriddenResultFactory = typeof(TResultFactory);
+ }
+ }
+}
\ No newline at end of file
diff --git a/FluentValidation.AutoValidation.Endpoints/src/Extensions/EndpointRouteExtensions.cs b/FluentValidation.AutoValidation.Endpoints/src/Extensions/EndpointRouteExtensions.cs
index 59401fa..e6f6fbf 100644
--- a/FluentValidation.AutoValidation.Endpoints/src/Extensions/EndpointRouteExtensions.cs
+++ b/FluentValidation.AutoValidation.Endpoints/src/Extensions/EndpointRouteExtensions.cs
@@ -8,7 +8,7 @@ namespace SharpGrip.FluentValidation.AutoValidation.Endpoints.Extensions
public static class EndpointRouteExtensions
{
///
- /// Adds asynchronous minimal API automatic validation to the specified .
+ /// Adds asynchronous minimal API automatic validation to the specified .
///
/// The route handler builder.
/// The route handler builder.
@@ -20,7 +20,7 @@ public static RouteHandlerBuilder AddFluentValidationAutoValidation(this RouteHa
}
///
- /// Adds asynchronous minimal API Fluent Validation automatic validation to the specified .
+ /// Adds asynchronous minimal API Fluent Validation automatic validation to the specified .
///
/// The route group builder.
/// The route group builder.
diff --git a/FluentValidation.AutoValidation.Endpoints/src/Extensions/ServiceCollectionExtensions.cs b/FluentValidation.AutoValidation.Endpoints/src/Extensions/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000..f4b0a82
--- /dev/null
+++ b/FluentValidation.AutoValidation.Endpoints/src/Extensions/ServiceCollectionExtensions.cs
@@ -0,0 +1,40 @@
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using SharpGrip.FluentValidation.AutoValidation.Endpoints.Configuration;
+using SharpGrip.FluentValidation.AutoValidation.Endpoints.Results;
+
+namespace SharpGrip.FluentValidation.AutoValidation.Endpoints.Extensions
+{
+ public static class ServiceCollectionExtensions
+ {
+ ///
+ /// Adds asynchronous Endpoints Fluent Validation automatic validation to the specified .
+ ///
+ /// The service collection.
+ /// The configuration delegate used to configure the FluentValidation AutoValidation Endpoints validation.
+ /// The service collection.
+ public static IServiceCollection AddFluentValidationAutoValidation(this IServiceCollection serviceCollection,
+ Action? autoValidationEndpointsConfiguration = null)
+ {
+ var configuration = new AutoValidationEndpointsConfiguration();
+
+ if (autoValidationEndpointsConfiguration != null)
+ {
+ autoValidationEndpointsConfiguration.Invoke(configuration);
+ serviceCollection.Configure(autoValidationEndpointsConfiguration);
+ }
+
+ // Add the default result factory.
+ serviceCollection.AddScoped();
+
+ // If the custom result factory is not null, replace the default result factory with the overridden result factory.
+ if (configuration.OverriddenResultFactory != null)
+ {
+ serviceCollection.Replace(new ServiceDescriptor(typeof(IFluentValidationAutoValidationResultFactory), configuration.OverriddenResultFactory, ServiceLifetime.Scoped));
+ }
+
+ return serviceCollection;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FluentValidation.AutoValidation.Endpoints/src/Filters/FluentValidationAutoValidationEndpointFilter.cs b/FluentValidation.AutoValidation.Endpoints/src/Filters/FluentValidationAutoValidationEndpointFilter.cs
index 574b650..b8d79c0 100644
--- a/FluentValidation.AutoValidation.Endpoints/src/Filters/FluentValidationAutoValidationEndpointFilter.cs
+++ b/FluentValidation.AutoValidation.Endpoints/src/Filters/FluentValidationAutoValidationEndpointFilter.cs
@@ -1,9 +1,9 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Threading.Tasks;
using FluentValidation;
using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using SharpGrip.FluentValidation.AutoValidation.Endpoints.Results;
using SharpGrip.FluentValidation.AutoValidation.Shared.Extensions;
namespace SharpGrip.FluentValidation.AutoValidation.Endpoints.Filters
@@ -29,14 +29,14 @@ public FluentValidationAutoValidationEndpointFilter(IServiceProvider serviceProv
if (!validationResult.IsValid)
{
- var errors = new Dictionary();
+ var fluentValidationAutoValidationResultFactory = serviceProvider.GetService();
- foreach (var errorGrouping in validationResult.Errors.GroupBy(error => error.PropertyName))
+ if (fluentValidationAutoValidationResultFactory != null)
{
- errors.Add(errorGrouping.Key, errorGrouping.Select(error => error.ErrorMessage).ToArray());
+ return fluentValidationAutoValidationResultFactory.CreateResult(context, validationResult);
}
- return Results.ValidationProblem(errors);
+ return new FluentValidationAutoValidationDefaultResultFactory().CreateResult(context, validationResult);
}
}
}
diff --git a/FluentValidation.AutoValidation.Endpoints/src/Results/FluentValidationAutoValidationDefaultResultFactory.cs b/FluentValidation.AutoValidation.Endpoints/src/Results/FluentValidationAutoValidationDefaultResultFactory.cs
new file mode 100644
index 0000000..30626be
--- /dev/null
+++ b/FluentValidation.AutoValidation.Endpoints/src/Results/FluentValidationAutoValidationDefaultResultFactory.cs
@@ -0,0 +1,14 @@
+using FluentValidation.Results;
+using Microsoft.AspNetCore.Http;
+using SharpGrip.FluentValidation.AutoValidation.Shared.Extensions;
+
+namespace SharpGrip.FluentValidation.AutoValidation.Endpoints.Results
+{
+ public class FluentValidationAutoValidationDefaultResultFactory : IFluentValidationAutoValidationResultFactory
+ {
+ public IResult CreateResult(EndpointFilterInvocationContext context, ValidationResult validationResult)
+ {
+ return TypedResults.ValidationProblem(validationResult.ToValidationProblemErrors());
+ }
+ }
+}
\ No newline at end of file
diff --git a/FluentValidation.AutoValidation.Endpoints/src/Results/IFluentValidationAutoValidationResultFactory.cs b/FluentValidation.AutoValidation.Endpoints/src/Results/IFluentValidationAutoValidationResultFactory.cs
new file mode 100644
index 0000000..7cf4c99
--- /dev/null
+++ b/FluentValidation.AutoValidation.Endpoints/src/Results/IFluentValidationAutoValidationResultFactory.cs
@@ -0,0 +1,16 @@
+using FluentValidation.Results;
+using Microsoft.AspNetCore.Http;
+
+namespace SharpGrip.FluentValidation.AutoValidation.Endpoints.Results
+{
+ public interface IFluentValidationAutoValidationResultFactory
+ {
+ ///
+ /// Creates an object to be executed by the endpoint.
+ ///
+ /// The associated with the current request/response.
+ /// The object containing the validation failures.
+ /// The object to be executed by the endpoint.
+ public IResult CreateResult(EndpointFilterInvocationContext context, ValidationResult validationResult);
+ }
+}
\ No newline at end of file
diff --git a/FluentValidation.AutoValidation.Mvc/src/Configuration/AutoValidationMvcConfiguration.cs b/FluentValidation.AutoValidation.Mvc/src/Configuration/AutoValidationMvcConfiguration.cs
index 63ff84d..056e341 100644
--- a/FluentValidation.AutoValidation.Mvc/src/Configuration/AutoValidationMvcConfiguration.cs
+++ b/FluentValidation.AutoValidation.Mvc/src/Configuration/AutoValidationMvcConfiguration.cs
@@ -1,18 +1,38 @@
-using SharpGrip.FluentValidation.AutoValidation.Mvc.Enums;
+using System;
+using Microsoft.AspNetCore.Mvc;
+using SharpGrip.FluentValidation.AutoValidation.Mvc.Attributes;
+using SharpGrip.FluentValidation.AutoValidation.Mvc.Enums;
+using SharpGrip.FluentValidation.AutoValidation.Mvc.Results;
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Configuration
{
public class AutoValidationMvcConfiguration
{
///
- /// Disables the built-in model validation.
+ /// Disables the built-in .NET model (data annotations) validation.
///
public bool DisableBuiltInModelValidation { get; set; }
///
- /// Configures the validation strategy. Validation strategy enables asynchronous automatic validation on all controllers inheriting from .
- /// Validation strategy enables asynchronous automatic validation on controllers inheriting from decorated (class or method) with a attribute.
+ /// Configures the validation strategy. Validation strategy enables asynchronous automatic validation on all controllers inheriting from .
+ /// Validation strategy enables asynchronous automatic validation on controllers inheriting from decorated (class or method) with a attribute.
///
public ValidationStrategy ValidationStrategy { get; set; } = ValidationStrategy.All;
+
+ ///
+ /// Holds the overridden result factory. This property is meant for infrastructure and should not be used by application code.
+ ///
+ public Type? OverriddenResultFactory { get; private set; }
+
+ ///
+ /// Overrides the default result factory with a custom result factory. Custom result factories are required to implement .
+ /// The default result factory returns the default object wrapped in a >.
+ ///
+ ///
+ /// The custom result factory implement .
+ public void OverrideDefaultResultFactoryWith() where TResultFactory : IFluentValidationAutoValidationResultFactory
+ {
+ OverriddenResultFactory = typeof(TResultFactory);
+ }
}
}
\ No newline at end of file
diff --git a/FluentValidation.AutoValidation.Mvc/src/Enums/ValidationStrategy.cs b/FluentValidation.AutoValidation.Mvc/src/Enums/ValidationStrategy.cs
index 88eb7a2..afdd392 100644
--- a/FluentValidation.AutoValidation.Mvc/src/Enums/ValidationStrategy.cs
+++ b/FluentValidation.AutoValidation.Mvc/src/Enums/ValidationStrategy.cs
@@ -1,14 +1,17 @@
-namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Enums
+using Microsoft.AspNetCore.Mvc;
+using SharpGrip.FluentValidation.AutoValidation.Mvc.Attributes;
+
+namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Enums
{
public enum ValidationStrategy
{
///
- /// Enables asynchronous automatic validation on all controllers inheriting from .
+ /// Enables asynchronous automatic validation on all controllers inheriting from .
///
All = 1,
///
- /// Enables asynchronous automatic validation on controllers inheriting from decorated with a attribute.
+ /// Enables asynchronous automatic validation on controllers inheriting from decorated with a attribute.
///
Annotations = 2
}
diff --git a/FluentValidation.AutoValidation.Mvc/src/Extensions/ServiceCollectionExtensions.cs b/FluentValidation.AutoValidation.Mvc/src/Extensions/ServiceCollectionExtensions.cs
index dcaf3cd..9f2cc79 100644
--- a/FluentValidation.AutoValidation.Mvc/src/Extensions/ServiceCollectionExtensions.cs
+++ b/FluentValidation.AutoValidation.Mvc/src/Extensions/ServiceCollectionExtensions.cs
@@ -4,10 +4,12 @@
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using SharpGrip.FluentValidation.AutoValidation.Mvc.Configuration;
using SharpGrip.FluentValidation.AutoValidation.Mvc.Filters;
+using SharpGrip.FluentValidation.AutoValidation.Mvc.Results;
using SharpGrip.FluentValidation.AutoValidation.Mvc.Validation;
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Extensions
@@ -15,7 +17,7 @@ namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Extensions
public static class ServiceCollectionExtensions
{
///
- /// Adds asynchronous MVC Fluent Validation automatic validation to the specified .
+ /// Adds asynchronous MVC Fluent Validation automatic validation to the specified .
///
/// The service collection.
/// The configuration delegate used to configure the FluentValidation AutoValidation MVC validation.
@@ -39,6 +41,15 @@ public static IServiceCollection AddFluentValidationAutoValidation(this IService
configuration.DisableBuiltInModelValidation));
}
+ // Add the default result factory.
+ serviceCollection.AddScoped();
+
+ // If the custom result factory is not null, replace the default result factory with the overridden result factory.
+ if (configuration.OverriddenResultFactory != null)
+ {
+ serviceCollection.Replace(new ServiceDescriptor(typeof(IFluentValidationAutoValidationResultFactory), configuration.OverriddenResultFactory, ServiceLifetime.Scoped));
+ }
+
// Create a default instance of the `ModelStateInvalidFilter` to access the non static property `Order` in a static context.
var modelStateInvalidFilter = new ModelStateInvalidFilter(new ApiBehaviorOptions {InvalidModelStateResponseFactory = context => new OkResult()}, NullLogger.Instance);
diff --git a/FluentValidation.AutoValidation.Mvc/src/Filters/FluentValidationAutoValidationActionFilter.cs b/FluentValidation.AutoValidation.Mvc/src/Filters/FluentValidationAutoValidationActionFilter.cs
index f3741ba..964cce0 100644
--- a/FluentValidation.AutoValidation.Mvc/src/Filters/FluentValidationAutoValidationActionFilter.cs
+++ b/FluentValidation.AutoValidation.Mvc/src/Filters/FluentValidationAutoValidationActionFilter.cs
@@ -4,12 +4,14 @@
using FluentValidation;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Options;
using SharpGrip.FluentValidation.AutoValidation.Mvc.Attributes;
using SharpGrip.FluentValidation.AutoValidation.Mvc.Configuration;
using SharpGrip.FluentValidation.AutoValidation.Mvc.Enums;
+using SharpGrip.FluentValidation.AutoValidation.Mvc.Results;
using SharpGrip.FluentValidation.AutoValidation.Shared.Extensions;
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Filters
@@ -17,11 +19,15 @@ namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Filters
public class FluentValidationAutoValidationActionFilter : IAsyncActionFilter
{
private readonly IServiceProvider serviceProvider;
+ private readonly IFluentValidationAutoValidationResultFactory fluentValidationAutoValidationResultFactory;
private readonly AutoValidationMvcConfiguration autoValidationMvcConfiguration;
- public FluentValidationAutoValidationActionFilter(IServiceProvider serviceProvider, IOptions autoValidationMvcConfiguration)
+ public FluentValidationAutoValidationActionFilter(IServiceProvider serviceProvider,
+ IFluentValidationAutoValidationResultFactory fluentValidationAutoValidationResultFactory,
+ IOptions autoValidationMvcConfiguration)
{
this.serviceProvider = serviceProvider;
+ this.fluentValidationAutoValidationResultFactory = fluentValidationAutoValidationResultFactory;
this.autoValidationMvcConfiguration = autoValidationMvcConfiguration.Value;
}
@@ -29,8 +35,8 @@ public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionE
{
if (context.Controller is ControllerBase controllerBase)
{
- var actionDescriptor = context.ActionDescriptor;
var endpoint = context.HttpContext.GetEndpoint();
+ var controllerActionDescriptor = (ControllerActionDescriptor) context.ActionDescriptor;
if (autoValidationMvcConfiguration.ValidationStrategy == ValidationStrategy.Annotations && !(endpoint?.Metadata.OfType().Any() ?? false))
{
@@ -39,16 +45,13 @@ public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionE
return;
}
- foreach (var parameter in actionDescriptor.Parameters)
+ foreach (var parameter in controllerActionDescriptor.Parameters)
{
if (context.ActionArguments.TryGetValue(parameter.Name, out var subject))
{
var parameterType = parameter.ParameterType;
-
- // ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
var bindingSource = parameter.BindingInfo?.BindingSource;
- // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
if (subject != null && (bindingSource == BindingSource.Body || (bindingSource == BindingSource.Query && parameterType.IsClass)))
{
if (serviceProvider.GetValidator(parameterType) is IValidator validator)
@@ -69,9 +72,9 @@ public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionE
if (!context.ModelState.IsValid)
{
- var validationProblem = controllerBase.ProblemDetailsFactory.CreateValidationProblemDetails(context.HttpContext, context.ModelState);
+ var validationProblemDetails = controllerBase.ProblemDetailsFactory.CreateValidationProblemDetails(context.HttpContext, context.ModelState);
- context.Result = new BadRequestObjectResult(validationProblem);
+ context.Result = fluentValidationAutoValidationResultFactory.CreateActionResult(context, validationProblemDetails);
return;
}
diff --git a/FluentValidation.AutoValidation.Mvc/src/Results/FluentValidationAutoValidationDefaultResultFactory.cs b/FluentValidation.AutoValidation.Mvc/src/Results/FluentValidationAutoValidationDefaultResultFactory.cs
new file mode 100644
index 0000000..bef32b9
--- /dev/null
+++ b/FluentValidation.AutoValidation.Mvc/src/Results/FluentValidationAutoValidationDefaultResultFactory.cs
@@ -0,0 +1,13 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+
+namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Results
+{
+ public class FluentValidationAutoValidationDefaultResultFactory : IFluentValidationAutoValidationResultFactory
+ {
+ public IActionResult CreateActionResult(ActionExecutingContext context, ValidationProblemDetails? validationProblemDetails)
+ {
+ return new BadRequestObjectResult(validationProblemDetails);
+ }
+ }
+}
\ No newline at end of file
diff --git a/FluentValidation.AutoValidation.Mvc/src/Results/IFluentValidationAutoValidationResultFactory.cs b/FluentValidation.AutoValidation.Mvc/src/Results/IFluentValidationAutoValidationResultFactory.cs
new file mode 100644
index 0000000..a106f1a
--- /dev/null
+++ b/FluentValidation.AutoValidation.Mvc/src/Results/IFluentValidationAutoValidationResultFactory.cs
@@ -0,0 +1,16 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+
+namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Results
+{
+ public interface IFluentValidationAutoValidationResultFactory
+ {
+ ///
+ /// Creates a to be executed by the controller action.
+ ///
+ /// The associated with the current request/response.
+ /// The instance object containing the validation failures.
+ /// The object to be executed by the controller action.
+ public IActionResult CreateActionResult(ActionExecutingContext context, ValidationProblemDetails? validationProblemDetails);
+ }
+}
\ No newline at end of file
diff --git a/FluentValidation.AutoValidation.Mvc/src/Validation/FluentValidationObjectModelValidator.cs b/FluentValidation.AutoValidation.Mvc/src/Validation/FluentValidationAutoValidationObjectModelValidator.cs
similarity index 100%
rename from FluentValidation.AutoValidation.Mvc/src/Validation/FluentValidationObjectModelValidator.cs
rename to FluentValidation.AutoValidation.Mvc/src/Validation/FluentValidationAutoValidationObjectModelValidator.cs
diff --git a/FluentValidation.AutoValidation.Shared/FluentValidation.AutoValidation.Shared.csproj b/FluentValidation.AutoValidation.Shared/FluentValidation.AutoValidation.Shared.csproj
index 138f731..7b86d38 100644
--- a/FluentValidation.AutoValidation.Shared/FluentValidation.AutoValidation.Shared.csproj
+++ b/FluentValidation.AutoValidation.Shared/FluentValidation.AutoValidation.Shared.csproj
@@ -10,7 +10,7 @@
SharpGrip.FluentValidation.AutoValidation.Shared
SharpGrip FluentValidation AutoValidation Shared
SharpGrip FluentValidation AutoValidation is an extension of the FluentValidation library enabling automatic asynchronous validation in MVC controllers and minimal APIs (endpoints).
- sharpgrip;validation;fluent-validation;mvc;minimal-api
+ sharpgrip;validation;fluent-validation;mvc;endpoints;minimal-api
diff --git a/FluentValidation.AutoValidation.Shared/src/Extensions/ValidationResultExtensions.cs b/FluentValidation.AutoValidation.Shared/src/Extensions/ValidationResultExtensions.cs
new file mode 100644
index 0000000..1794022
--- /dev/null
+++ b/FluentValidation.AutoValidation.Shared/src/Extensions/ValidationResultExtensions.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+using System.Linq;
+using FluentValidation.Results;
+
+namespace SharpGrip.FluentValidation.AutoValidation.Shared.Extensions
+{
+ public static class ValidationResultExtensions
+ {
+ public static IDictionary ToValidationProblemErrors(this ValidationResult validationResult)
+ {
+ return validationResult.Errors.GroupBy(validationFailure => validationFailure.PropertyName)
+ .ToDictionary(validationFailureGrouping => validationFailureGrouping.Key,
+ validationFailureGrouping => validationFailureGrouping.Select(validationFailure => validationFailure.ErrorMessage).ToArray());
+ }
+ }
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 8d063e8..fea0ed7 100644
--- a/README.md
+++ b/README.md
@@ -12,12 +12,9 @@
## Introduction
-SharpGrip FluentValidation AutoValidation is an extension of the [FluentValidation](https://github.com/FluentValidation/FluentValidation) library enabling automatic asynchronous validation in MVC
-controllers and minimal APIs (endpoints).
-The library [FluentValidation.AspNetCore](https://github.com/FluentValidation/FluentValidation.AspNetCore) is no longer being maintained and is unsupported. As a result, support for automatic
-validation provided by this library is no longer available.
-This library re-introduces this functionality for MVC controllers and introduces automation validation for minimal APIs (endpoints). It enables developers to easily implement automatic validation in
-their projects.
+SharpGrip FluentValidation AutoValidation is an extension of the [FluentValidation](https://github.com/FluentValidation/FluentValidation) (v10+) library enabling automatic asynchronous validation in MVC controllers and minimal APIs (endpoints).
+The library [FluentValidation.AspNetCore](https://github.com/FluentValidation/FluentValidation.AspNetCore) is no longer being maintained and is unsupported. As a result, support for automatic validation provided by this library is no longer available.
+This library re-introduces this functionality for MVC controllers and introduces automation validation for minimal APIs (endpoints). It enables developers to easily implement automatic validation in their projects.
## Installation
@@ -33,7 +30,7 @@ using SharpGrip.FluentValidation.AutoValidation.Mvc.Extensions;
builder.Services.AddFluentValidationAutoValidation();
```
-### Minimal APIs [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FluentValidation.AutoValidation.Endpoints)](https://www.nuget.org/packages/SharpGrip.FluentValidation.AutoValidation.Endpoints)
+### Minimal APIs (endpoints) [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FluentValidation.AutoValidation.Endpoints)](https://www.nuget.org/packages/SharpGrip.FluentValidation.AutoValidation.Endpoints)
For minimal APIs (endpoints) reference NuGet package `SharpGrip.FluentValidation.AutoValidation.Endpoints` (https://www.nuget.org/packages/SharpGrip.FluentValidation.AutoValidation.Endpoints).
@@ -42,6 +39,9 @@ Enabling minimal API (endpoint) automatic validation can be done on both route g
```
using SharpGrip.FluentValidation.AutoValidation.Endpoints.Extensions;
+builder.Services.AddFluentValidationAutoValidation();
+
+var app = builder.Build();
var endpointGroup = app.MapGroup("/some-group").AddFluentValidationAutoValidation();
endpointGroup.MapPost("/", (SomeModel someModel) => $"Hello {someModel.Name}");
@@ -54,7 +54,7 @@ app.MapPost("/", (SomeOtherModel someOtherModel) => $"Hello again {someOtherMode
| Property | Default value | Description |
|-------------------------------|--------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| DisableBuiltInModelValidation | `false` | Disables the built-in model validation. |
+| DisableBuiltInModelValidation | `false` | Disables the built-in .NET model (data annotations) validation. |
| ValidationStrategy | `ValidationStrategy.All` | Configures the validation strategy. Validation strategy `ValidationStrategy.All` enables asynchronous automatic validation on all controllers inheriting from `ControllerBase`. Validation strategy `ValidationStrategy.Annotations` enables asynchronous automatic validation on controllers inheriting from `ControllerBase` decorated (class or method) with a `FluentValidationAutoValidationAttribute` attribute. |
```
@@ -62,9 +62,42 @@ using SharpGrip.FluentValidation.AutoValidation.Mvc.Extensions;
builder.Services.AddFluentValidationAutoValidation(configuration =>
{
- configuration.DisableDataAnnotationsValidation = true;
+ // Disable the built-in .NET model (data annotations) validation.
+ configuration.DisableBuiltInModelValidation = true;
// Only validate controllers decorated with the `FluentValidationAutoValidation` attribute.
configuration.ValidationStrategy = ValidationStrategy.Annotation;
+
+ // Replace the default result factory with a custom implementation.
+ configuration.OverrideDefaultResultFactoryWith();
});
-```
\ No newline at end of file
+
+public class CustomResultFactory : IFluentValidationAutoValidationResultFactory
+{
+ public IActionResult CreateActionResult(ActionExecutingContext context, ValidationProblemDetails? validationProblemDetails)
+ {
+ return new BadRequestObjectResult(new {Title = "Validation errors", ValidationErrors = validationProblemDetails?.Errors});
+ }
+}
+```
+
+### Minimal APIs (endpoints)
+
+```
+using SharpGrip.FluentValidation.AutoValidation.Endpoints.Extensions;
+
+builder.Services.AddFluentValidationAutoValidation(configuration =>
+{
+ // Replace the default result factory with a custom implementation.
+ configuration.OverrideDefaultResultFactoryWith();
+});
+
+public class CustomResultFactory : IFluentValidationAutoValidationResultFactory
+{
+ public IResult CreateResult(EndpointFilterInvocationContext context, ValidationResult validationResult)
+ {
+ var validationProblemErrors = validationResult.ToValidationProblemErrors();
+ return Results.ValidationProblem(validationProblemErrors, "Some details text.", "Some instance text.", (int) HttpStatusCode.BadRequest, "Some title.");
+ }
+}
+```