diff --git a/FluentValidation.AutoValidation.Endpoints/src/Extensions/ServiceCollectionExtensions.cs b/FluentValidation.AutoValidation.Endpoints/src/Extensions/ServiceCollectionExtensions.cs
index f4b0a82..5d4250a 100644
--- a/FluentValidation.AutoValidation.Endpoints/src/Extensions/ServiceCollectionExtensions.cs
+++ b/FluentValidation.AutoValidation.Endpoints/src/Extensions/ServiceCollectionExtensions.cs
@@ -14,8 +14,7 @@ public static class ServiceCollectionExtensions
/// 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)
+ public static IServiceCollection AddFluentValidationAutoValidation(this IServiceCollection serviceCollection, Action? autoValidationEndpointsConfiguration = null)
{
var configuration = new AutoValidationEndpointsConfiguration();
diff --git a/FluentValidation.AutoValidation.Endpoints/src/Filters/FluentValidationAutoValidationEndpointFilter.cs b/FluentValidation.AutoValidation.Endpoints/src/Filters/FluentValidationAutoValidationEndpointFilter.cs
index 82bb295..5b9fa37 100644
--- a/FluentValidation.AutoValidation.Endpoints/src/Filters/FluentValidationAutoValidationEndpointFilter.cs
+++ b/FluentValidation.AutoValidation.Endpoints/src/Filters/FluentValidationAutoValidationEndpointFilter.cs
@@ -2,13 +2,14 @@
using FluentValidation;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
using SharpGrip.FluentValidation.AutoValidation.Endpoints.Interceptors;
using SharpGrip.FluentValidation.AutoValidation.Endpoints.Results;
using SharpGrip.FluentValidation.AutoValidation.Shared.Extensions;
namespace SharpGrip.FluentValidation.AutoValidation.Endpoints.Filters
{
- public class FluentValidationAutoValidationEndpointFilter : IEndpointFilter
+ public class FluentValidationAutoValidationEndpointFilter(ILogger logger) : IEndpointFilter
{
public async ValueTask InvokeAsync(EndpointFilterInvocationContext endpointFilterInvocationContext, EndpointFilterDelegate next)
{
@@ -18,6 +19,8 @@ public class FluentValidationAutoValidationEndpointFilter : IEndpointFilter
{
if (argument != null && argument.GetType().IsCustomType() && serviceProvider.GetValidator(argument.GetType()) is IValidator validator)
{
+ logger.LogDebug("Starting validation for argument of type '{Type}'.", argument.GetType().Name);
+
var validatorInterceptor = validator as IValidatorInterceptor;
var globalValidationInterceptor = serviceProvider.GetService();
@@ -25,11 +28,13 @@ public class FluentValidationAutoValidationEndpointFilter : IEndpointFilter
if (validatorInterceptor != null)
{
+ logger.LogDebug("Invoking validator interceptor BeforeValidation for argument '{Argument}'.", argument.GetType().Name);
validationContext = await validatorInterceptor.BeforeValidation(endpointFilterInvocationContext, validationContext, endpointFilterInvocationContext.HttpContext.RequestAborted) ?? validationContext;
}
if (globalValidationInterceptor != null)
{
+ logger.LogDebug("Invoking global validation interceptor BeforeValidation for argument '{Argument}'.", argument.GetType().Name);
validationContext = await globalValidationInterceptor.BeforeValidation(endpointFilterInvocationContext, validationContext, endpointFilterInvocationContext.HttpContext.RequestAborted) ?? validationContext;
}
@@ -37,25 +42,37 @@ public class FluentValidationAutoValidationEndpointFilter : IEndpointFilter
if (validatorInterceptor != null)
{
+ logger.LogDebug("Invoking validator interceptor AfterValidation for argument '{Argument}'.", argument.GetType().Name);
validationResult = await validatorInterceptor.AfterValidation(endpointFilterInvocationContext, validationContext, validationResult, endpointFilterInvocationContext.HttpContext.RequestAborted) ?? validationResult;
}
if (globalValidationInterceptor != null)
{
+ logger.LogDebug("Invoking global validation interceptor AfterValidation for argument '{Argument}'.", argument.GetType().Name);
validationResult = await globalValidationInterceptor.AfterValidation(endpointFilterInvocationContext, validationContext, validationResult, endpointFilterInvocationContext.HttpContext.RequestAborted) ?? validationResult;
}
if (!validationResult.IsValid)
{
+ logger.LogDebug("Validation result not valid for argument '{Argument}': {ErrorCount} validation errors found.", argument.GetType().Name, validationResult.Errors.Count);
+
var fluentValidationAutoValidationResultFactory = serviceProvider.GetService();
+ logger.LogDebug("Creating result for path '{Path}'.", endpointFilterInvocationContext.HttpContext.Request.Path);
+
if (fluentValidationAutoValidationResultFactory != null)
{
+ logger.LogTrace("Creating result for path '{Path}' using a custom result factory.", endpointFilterInvocationContext.HttpContext.Request.Path);
+
return fluentValidationAutoValidationResultFactory.CreateResult(endpointFilterInvocationContext, validationResult);
}
+ logger.LogTrace("Creating result for path '{Path}' using the default result factory.", endpointFilterInvocationContext.HttpContext.Request.Path);
+
return new FluentValidationAutoValidationDefaultResultFactory().CreateResult(endpointFilterInvocationContext, validationResult);
}
+
+ logger.LogDebug("Validation result valid for argument '{Argument}'.", argument.GetType().Name);
}
}
diff --git a/FluentValidation.AutoValidation.Mvc/src/Attributes/AutoValidateAlwaysAttribute.cs b/FluentValidation.AutoValidation.Mvc/src/Attributes/AutoValidateAlwaysAttribute.cs
index 173d93d..c866642 100644
--- a/FluentValidation.AutoValidation.Mvc/src/Attributes/AutoValidateAlwaysAttribute.cs
+++ b/FluentValidation.AutoValidation.Mvc/src/Attributes/AutoValidateAlwaysAttribute.cs
@@ -3,7 +3,5 @@
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Attributes
{
[AttributeUsage(AttributeTargets.Parameter)]
- public class AutoValidateAlwaysAttribute : Attribute
- {
- }
+ public class AutoValidateAlwaysAttribute : Attribute;
}
\ No newline at end of file
diff --git a/FluentValidation.AutoValidation.Mvc/src/Attributes/AutoValidateNeverAttribute.cs b/FluentValidation.AutoValidation.Mvc/src/Attributes/AutoValidateNeverAttribute.cs
index 186e61f..dce6a53 100644
--- a/FluentValidation.AutoValidation.Mvc/src/Attributes/AutoValidateNeverAttribute.cs
+++ b/FluentValidation.AutoValidation.Mvc/src/Attributes/AutoValidateNeverAttribute.cs
@@ -3,7 +3,5 @@
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Attributes
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Parameter)]
- public class AutoValidateNeverAttribute : Attribute
- {
- }
+ public class AutoValidateNeverAttribute : Attribute;
}
\ No newline at end of file
diff --git a/FluentValidation.AutoValidation.Mvc/src/Attributes/AutoValidationAttribute.cs b/FluentValidation.AutoValidation.Mvc/src/Attributes/AutoValidationAttribute.cs
index 799ba08..eaaf12c 100644
--- a/FluentValidation.AutoValidation.Mvc/src/Attributes/AutoValidationAttribute.cs
+++ b/FluentValidation.AutoValidation.Mvc/src/Attributes/AutoValidationAttribute.cs
@@ -3,7 +3,5 @@
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Attributes
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
- public class AutoValidationAttribute : Attribute
- {
- }
+ public class AutoValidationAttribute : Attribute;
}
\ No newline at end of file
diff --git a/FluentValidation.AutoValidation.Mvc/src/Extensions/ServiceCollectionExtensions.cs b/FluentValidation.AutoValidation.Mvc/src/Extensions/ServiceCollectionExtensions.cs
index 5009139..1235b6b 100644
--- a/FluentValidation.AutoValidation.Mvc/src/Extensions/ServiceCollectionExtensions.cs
+++ b/FluentValidation.AutoValidation.Mvc/src/Extensions/ServiceCollectionExtensions.cs
@@ -34,11 +34,7 @@ public static IServiceCollection AddFluentValidationAutoValidation(this IService
if (configuration.DisableBuiltInModelValidation)
{
- serviceCollection.AddSingleton(serviceProvider =>
- new FluentValidationAutoValidationObjectModelValidator(
- serviceProvider.GetRequiredService(),
- serviceProvider.GetRequiredService>().Value.ModelValidatorProviders,
- configuration.DisableBuiltInModelValidation));
+ serviceCollection.AddSingleton(serviceProvider => new FluentValidationAutoValidationObjectModelValidator(serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService>().Value.ModelValidatorProviders, configuration.DisableBuiltInModelValidation));
}
// Add the default result factory.
diff --git a/FluentValidation.AutoValidation.Mvc/src/Filters/FluentValidationAutoValidationActionFilter.cs b/FluentValidation.AutoValidation.Mvc/src/Filters/FluentValidationAutoValidationActionFilter.cs
index 1dfccd2..17714c5 100644
--- a/FluentValidation.AutoValidation.Mvc/src/Filters/FluentValidationAutoValidationActionFilter.cs
+++ b/FluentValidation.AutoValidation.Mvc/src/Filters/FluentValidationAutoValidationActionFilter.cs
@@ -11,6 +11,7 @@
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SharpGrip.FluentValidation.AutoValidation.Mvc.Attributes;
using SharpGrip.FluentValidation.AutoValidation.Mvc.Configuration;
@@ -21,27 +22,25 @@
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Filters
{
- public class FluentValidationAutoValidationActionFilter : IAsyncActionFilter
+ public class FluentValidationAutoValidationActionFilter(IFluentValidationAutoValidationResultFactory fluentValidationAutoValidationResultFactory, IOptions autoValidationMvcConfiguration, ILogger logger) : IAsyncActionFilter
{
- private readonly IFluentValidationAutoValidationResultFactory fluentValidationAutoValidationResultFactory;
- private readonly AutoValidationMvcConfiguration autoValidationMvcConfiguration;
-
- public FluentValidationAutoValidationActionFilter(IFluentValidationAutoValidationResultFactory fluentValidationAutoValidationResultFactory, IOptions autoValidationMvcConfiguration)
- {
- this.fluentValidationAutoValidationResultFactory = fluentValidationAutoValidationResultFactory;
- this.autoValidationMvcConfiguration = autoValidationMvcConfiguration.Value;
- }
+ private readonly AutoValidationMvcConfiguration autoValidationMvcConfiguration = autoValidationMvcConfiguration.Value;
public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutingContext, ActionExecutionDelegate next)
{
+ var controllerActionDescriptor = (ControllerActionDescriptor) actionExecutingContext.ActionDescriptor;
+
+ logger.LogDebug("Starting validation for action '{Action}' on controller '{Controller}'.", controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
+
if (IsValidController(actionExecutingContext.Controller))
{
var endpoint = actionExecutingContext.HttpContext.GetEndpoint();
- var controllerActionDescriptor = (ControllerActionDescriptor) actionExecutingContext.ActionDescriptor;
var serviceProvider = actionExecutingContext.HttpContext.RequestServices;
if (endpoint != null && ((autoValidationMvcConfiguration.ValidationStrategy == ValidationStrategy.Annotations && !endpoint.Metadata.OfType().Any()) || endpoint.Metadata.OfType().Any()))
{
+ logger.LogDebug("Skipping validation for action '{Action}' on controller '{Controller}' due to validation strategy or AutoValidateNeverAttribute.", controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
+
HandleUnvalidatedEntries(actionExecutingContext);
await next();
@@ -64,6 +63,8 @@ public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutingC
if (subject != null && parameterType != null && parameterType.IsCustomType() && !hasAutoValidateNeverAttribute && (hasAutoValidateAlwaysAttribute || HasValidBindingSource(bindingSource)) && serviceProvider.GetValidator(parameterType) is IValidator validator)
{
+ logger.LogDebug("Validating parameter '{Parameter}' of type '{Type}' for action '{Action}' on controller '{Controller}'.", parameter.Name, parameterType.Name, controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
+
// ReSharper disable once SuspiciousTypeConversion.Global
var validatorInterceptor = validator as IValidatorInterceptor;
var globalValidationInterceptor = serviceProvider.GetService();
@@ -72,11 +73,13 @@ public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutingC
if (validatorInterceptor != null)
{
+ logger.LogDebug("Invoking validator interceptor BeforeValidation for parameter '{Parameter}'.", parameter.Name);
validationContext = await validatorInterceptor.BeforeValidation(actionExecutingContext, validationContext) ?? validationContext;
}
if (globalValidationInterceptor != null)
{
+ logger.LogDebug("Invoking global validation interceptor BeforeValidation for parameter '{Parameter}'.", parameter.Name);
validationContext = await globalValidationInterceptor.BeforeValidation(actionExecutingContext, validationContext) ?? validationContext;
}
@@ -85,21 +88,31 @@ public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutingC
if (validatorInterceptor != null)
{
+ logger.LogDebug("Invoking validator interceptor AfterValidation for parameter '{Parameter}'.", parameter.Name);
validationResult = await validatorInterceptor.AfterValidation(actionExecutingContext, validationContext, validationResult) ?? validationResult;
}
if (globalValidationInterceptor != null)
{
+ logger.LogDebug("Invoking global validation interceptor AfterValidation for parameter '{Parameter}'.", parameter.Name);
validationResult = await globalValidationInterceptor.AfterValidation(actionExecutingContext, validationContext, validationResult) ?? validationResult;
}
if (!validationResult.IsValid)
{
+ logger.LogDebug("Validation result not valid for parameter '{Parameter}' of type '{Type}' for action '{Action}' on controller '{Controller}': {ErrorCount} validation errors found.", parameter.Name, parameterType.Name, controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName, validationResult.Errors.Count);
+
foreach (var error in validationResult.Errors)
{
+ logger.LogTrace("Adding validation error '{ErrorMessage}' for '{ParameterName}' to ModelState.", error.ErrorMessage, parameter.Name);
+
actionExecutingContext.ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
}
}
+ else
+ {
+ logger.LogDebug("Validation result valid for parameter '{Parameter}' of type '{Type}' for action '{Action}' on controller '{Controller}'.", parameter.Name, parameterType.Name, controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
+ }
}
}
}
@@ -108,13 +121,28 @@ public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutingC
if (!actionExecutingContext.ModelState.IsValid)
{
+ logger.LogDebug("ModelState is not valid for action '{Action}' on controller '{Controller}'. Creating validation problem details.", controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
+
var problemDetailsFactory = serviceProvider.GetRequiredService();
var validationProblemDetails = problemDetailsFactory.CreateValidationProblemDetails(actionExecutingContext.HttpContext, actionExecutingContext.ModelState);
+ logger.LogTrace("Creating action result for action '{Action}' on controller '{Controller}'.", controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
+
actionExecutingContext.Result = await fluentValidationAutoValidationResultFactory.CreateActionResult(actionExecutingContext, validationProblemDetails, validationResults);
+ if (actionExecutingContext.Result != null)
+ {
+ logger.LogTrace("Action result created for action '{Action}' on controller '{Controller}'.", controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
+ }
+ else
+ {
+ logger.LogTrace("No action result created for action '{Action}' on controller '{Controller}'.", controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
+ }
+
return;
}
+
+ logger.LogDebug("ModelState is valid for action '{Action}' on controller '{Controller}'. Proceeding with action execution.", controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
}
await next();
@@ -126,6 +154,8 @@ private bool IsValidController(object controller)
if (controllerType.HasCustomAttribute())
{
+ logger.LogDebug("Controller '{Controller}' is marked with NonControllerAttribute. Skipping validation.", controllerType.Name);
+
return false;
}
@@ -147,11 +177,17 @@ private void HandleUnvalidatedEntries(ActionExecutingContext context)
{
if (autoValidationMvcConfiguration.DisableBuiltInModelValidation)
{
+ logger.LogDebug("Skipping validation of unvalidated entries due to DisableBuiltInModelValidation being set to true.");
+
foreach (var modelStateEntry in context.ModelState.Values.Where(modelStateEntry => modelStateEntry.ValidationState == ModelValidationState.Unvalidated))
{
modelStateEntry.ValidationState = ModelValidationState.Skipped;
}
}
+ else
+ {
+ logger.LogDebug("Skipping validation of unvalidated entries due to DisableBuiltInModelValidation being set to false.");
+ }
}
}
}
\ No newline at end of file
diff --git a/FluentValidation.AutoValidation.Mvc/src/Validation/FluentValidationAutoValidationObjectModelValidator.cs b/FluentValidation.AutoValidation.Mvc/src/Validation/FluentValidationAutoValidationObjectModelValidator.cs
index 6c5c6b6..31104f4 100644
--- a/FluentValidation.AutoValidation.Mvc/src/Validation/FluentValidationAutoValidationObjectModelValidator.cs
+++ b/FluentValidation.AutoValidation.Mvc/src/Validation/FluentValidationAutoValidationObjectModelValidator.cs
@@ -5,21 +5,9 @@
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Validation
{
- public class FluentValidationAutoValidationObjectModelValidator : ObjectModelValidator
+ public class FluentValidationAutoValidationObjectModelValidator(IModelMetadataProvider modelMetadataProvider, IList validatorProviders, bool disableBuiltInModelValidation) : ObjectModelValidator(modelMetadataProvider, validatorProviders)
{
- private readonly bool disableBuiltInModelValidation;
-
- public FluentValidationAutoValidationObjectModelValidator(IModelMetadataProvider modelMetadataProvider, IList validatorProviders, bool disableBuiltInModelValidation)
- : base(modelMetadataProvider, validatorProviders)
- {
- this.disableBuiltInModelValidation = disableBuiltInModelValidation;
- }
-
- public override ValidationVisitor GetValidationVisitor(ActionContext actionContext,
- IModelValidatorProvider validatorProvider,
- ValidatorCache validatorCache,
- IModelMetadataProvider metadataProvider,
- ValidationStateDictionary? validationState)
+ public override ValidationVisitor GetValidationVisitor(ActionContext actionContext, IModelValidatorProvider validatorProvider, ValidatorCache validatorCache, IModelMetadataProvider metadataProvider, ValidationStateDictionary? validationState)
{
return new FluentValidationAutoValidationValidationVisitor(actionContext, validatorProvider, validatorCache, metadataProvider, validationState, disableBuiltInModelValidation);
}
diff --git a/FluentValidation.AutoValidation.Mvc/src/Validation/FluentValidationAutoValidationValidationVisitor.cs b/FluentValidation.AutoValidation.Mvc/src/Validation/FluentValidationAutoValidationValidationVisitor.cs
index b72f240..22f54ff 100644
--- a/FluentValidation.AutoValidation.Mvc/src/Validation/FluentValidationAutoValidationValidationVisitor.cs
+++ b/FluentValidation.AutoValidation.Mvc/src/Validation/FluentValidationAutoValidationValidationVisitor.cs
@@ -4,33 +4,12 @@
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Validation
{
- public class FluentValidationAutoValidationValidationVisitor : ValidationVisitor
+ public class FluentValidationAutoValidationValidationVisitor(ActionContext actionContext, IModelValidatorProvider validatorProvider, ValidatorCache validatorCache, IModelMetadataProvider metadataProvider, ValidationStateDictionary? validationState, bool disableBuiltInModelValidation) : ValidationVisitor(actionContext, validatorProvider, validatorCache, metadataProvider, validationState)
{
- private readonly bool disableBuiltInModelValidation;
-
- public FluentValidationAutoValidationValidationVisitor(ActionContext actionContext,
- IModelValidatorProvider validatorProvider,
- ValidatorCache validatorCache,
- IModelMetadataProvider metadataProvider,
- ValidationStateDictionary? validationState,
- bool disableBuiltInModelValidation)
- : base(actionContext, validatorProvider, validatorCache, metadataProvider, validationState)
- {
- this.disableBuiltInModelValidation = disableBuiltInModelValidation;
- }
-
- public override bool Validate(ModelMetadata? metadata, string? key, object? model, bool alwaysValidateAtTopLevel)
- {
- // If built in model validation is disabled return true for later validation in the action filter.
- return disableBuiltInModelValidation || base.Validate(metadata, key, model, alwaysValidateAtTopLevel);
- }
-
-#if !NETCOREAPP3_1
public override bool Validate(ModelMetadata? metadata, string? key, object? model, bool alwaysValidateAtTopLevel, object? container)
{
// If built in model validation is disabled return true for later validation in the action filter.
return disableBuiltInModelValidation || base.Validate(metadata, key, model, alwaysValidateAtTopLevel, container);
}
-#endif
}
}
\ No newline at end of file
diff --git a/README.md b/README.md
index 9de90bb..84be1da 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@
[](https://sonarcloud.io/summary/overall?id=SharpGrip_FluentValidation.AutoValidation)
## Upgrading
+
Check out the [upgrade guide](UPGRADING.md).
## Introduction
@@ -75,17 +76,17 @@ app.MapPost("/", (SomeOtherModel someOtherModel) => $"Hello again {someOtherMode
### MVC controllers
-| Property | Default value | Description |
-|----------------------------------------------|--------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| 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 `[AutoValidationAttribute]` attribute. |
-| EnableBodyBindingSourceAutomaticValidation | `true` | Enables asynchronous automatic validation for parameters bound from `BindingSource.Body` binding sources (typically parameters decorated with the `[FromBody]` attribute). |
-| EnableFormBindingSourceAutomaticValidation | `false` | Enables asynchronous automatic validation for parameters bound from `BindingSource.Form` binding sources (typically parameters decorated with the `[FromForm]` attribute). |
-| EnableQueryBindingSourceAutomaticValidation | `true` | Enables asynchronous automatic validation for parameters bound from `BindingSource.Query` binding sources (typically parameters decorated with the `[FromQuery]` attribute). |
-| EnablePathBindingSourceAutomaticValidation | `false` | Enables asynchronous automatic validation for parameters bound from `BindingSource.Path` binding sources (typically parameters decorated with the `[FromRoute]` attribute). |
-| EnableHeaderBindingSourceAutomaticValidation | `false` | Enables asynchronous automatic validation for parameters bound from `BindingSource.Header` binding sources (typically parameters decorated with the `[FromHeader]` attribute). |
-| EnableCustomBindingSourceAutomaticValidation | `false` | Enables asynchronous automatic validation for parameters bound from `BindingSource.Custom` binding sources. |
-| EnableNullBindingSourceAutomaticValidation | `false` | Enables asynchronous automatic validation for parameters not bound from any binding source (typically parameters without a declared or inferred binding source). |
+| Property | Default value | Description |
+|----------------------------------------------|--------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| 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. Validation strategy `ValidationStrategy.Annotations` enables asynchronous automatic validation on controllers decorated (class or method) with a `[AutoValidationAttribute]` attribute. |
+| EnableBodyBindingSourceAutomaticValidation | `true` | Enables asynchronous automatic validation for parameters bound from `BindingSource.Body` binding sources (typically parameters decorated with the `[FromBody]` attribute). |
+| EnableFormBindingSourceAutomaticValidation | `false` | Enables asynchronous automatic validation for parameters bound from `BindingSource.Form` binding sources (typically parameters decorated with the `[FromForm]` attribute). |
+| EnableQueryBindingSourceAutomaticValidation | `true` | Enables asynchronous automatic validation for parameters bound from `BindingSource.Query` binding sources (typically parameters decorated with the `[FromQuery]` attribute). |
+| EnablePathBindingSourceAutomaticValidation | `false` | Enables asynchronous automatic validation for parameters bound from `BindingSource.Path` binding sources (typically parameters decorated with the `[FromRoute]` attribute). |
+| EnableHeaderBindingSourceAutomaticValidation | `false` | Enables asynchronous automatic validation for parameters bound from `BindingSource.Header` binding sources (typically parameters decorated with the `[FromHeader]` attribute). |
+| EnableCustomBindingSourceAutomaticValidation | `false` | Enables asynchronous automatic validation for parameters bound from `BindingSource.Custom` binding sources. |
+| EnableNullBindingSourceAutomaticValidation | `false` | Enables asynchronous automatic validation for parameters not bound from any binding source (typically parameters without a declared or inferred binding source). |
```cs
using SharpGrip.FluentValidation.AutoValidation.Mvc.Extensions;
diff --git a/Tests/src/FluentValidation.AutoValidation.Endpoints/Filters/FluentValidationAutoValidationEndpointFilterTest.cs b/Tests/src/FluentValidation.AutoValidation.Endpoints/Filters/FluentValidationAutoValidationEndpointFilterTest.cs
index 44a3321..1836bab 100644
--- a/Tests/src/FluentValidation.AutoValidation.Endpoints/Filters/FluentValidationAutoValidationEndpointFilterTest.cs
+++ b/Tests/src/FluentValidation.AutoValidation.Endpoints/Filters/FluentValidationAutoValidationEndpointFilterTest.cs
@@ -9,6 +9,7 @@
using FluentValidation.Results;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.HttpResults;
+using Microsoft.Extensions.Logging;
using NSubstitute;
using SharpGrip.FluentValidation.AutoValidation.Endpoints.Filters;
using SharpGrip.FluentValidation.AutoValidation.Endpoints.Interceptors;
@@ -28,6 +29,7 @@ public class FluentValidationAutoValidationEndpointFilterTest
[Fact]
public async Task TestInvokeAsync_ValidatorFound()
{
+ var logger = Substitute.For>();
var serviceProvider = Substitute.For();
var endpointFilterInvocationContext = Substitute.For();
@@ -38,7 +40,7 @@ public async Task TestInvokeAsync_ValidatorFound()
var validationFailuresValues = ValidationFailures.Values.ToList();
- var endpointFilter = new FluentValidationAutoValidationEndpointFilter();
+ var endpointFilter = new FluentValidationAutoValidationEndpointFilter(logger);
var result = (ValidationProblem) (await endpointFilter.InvokeAsync(endpointFilterInvocationContext, _ => ValueTask.FromResult(new object())!))!;
var problemDetailsErrorValues = result.ProblemDetails.Errors.ToList();
@@ -51,6 +53,7 @@ public async Task TestInvokeAsync_ValidatorFound()
[Fact]
public async Task TestInvokeAsync_ValidatorNotFound()
{
+ var logger = Substitute.For>();
var serviceProvider = Substitute.For();
var endpointFilterInvocationContext = Substitute.For();
@@ -59,7 +62,7 @@ public async Task TestInvokeAsync_ValidatorNotFound()
serviceProvider.GetService(typeof(IValidator<>).MakeGenericType(typeof(TestModel))).Returns(null);
serviceProvider.GetService(typeof(IGlobalValidationInterceptor)).Returns(null);
- var endpointFilter = new FluentValidationAutoValidationEndpointFilter();
+ var endpointFilter = new FluentValidationAutoValidationEndpointFilter(logger);
var result = await endpointFilter.InvokeAsync(endpointFilterInvocationContext, _ => ValueTask.FromResult(new object())!);
diff --git a/Tests/src/FluentValidation.AutoValidation.Mvc/Filters/FluentValidationAutoValidationActionFilterTest.cs b/Tests/src/FluentValidation.AutoValidation.Mvc/Filters/FluentValidationAutoValidationActionFilterTest.cs
index 18bbba0..93e85c3 100644
--- a/Tests/src/FluentValidation.AutoValidation.Mvc/Filters/FluentValidationAutoValidationActionFilterTest.cs
+++ b/Tests/src/FluentValidation.AutoValidation.Mvc/Filters/FluentValidationAutoValidationActionFilterTest.cs
@@ -13,6 +13,7 @@
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NSubstitute;
using SharpGrip.FluentValidation.AutoValidation.Mvc.Configuration;
@@ -66,6 +67,7 @@ public async Task TestOnActionExecutionAsync()
var problemDetailsFactory = Substitute.For();
var fluentValidationAutoValidationResultFactory = Substitute.For();
var autoValidationMvcConfiguration = Substitute.For>();
+ var logger = Substitute.For>();
var httpContext = Substitute.For();
var controller = Substitute.For();
var actionContext = Substitute.For(httpContext, Substitute.For(), controllerActionDescriptor, modelStateDictionary);
@@ -84,7 +86,7 @@ public async Task TestOnActionExecutionAsync()
actionExecutingContext.ActionArguments.Returns(actionArguments);
autoValidationMvcConfiguration.Value.Returns(new AutoValidationMvcConfiguration());
- var actionFilter = new FluentValidationAutoValidationActionFilter(fluentValidationAutoValidationResultFactory, autoValidationMvcConfiguration);
+ var actionFilter = new FluentValidationAutoValidationActionFilter(fluentValidationAutoValidationResultFactory, autoValidationMvcConfiguration, logger);
await actionFilter.OnActionExecutionAsync(actionExecutingContext, () => Task.FromResult(actionExecutedContext));
@@ -162,7 +164,9 @@ public async Task OnActionExecutionAsync_WithInstanceTypeDifferentThanParameterT
var autoValidationMvcConfiguration = Substitute.For>();
autoValidationMvcConfiguration.Value.Returns(new AutoValidationMvcConfiguration());
- var actionFilter = new FluentValidationAutoValidationActionFilter(fluentValidationAutoValidationResultFactory, autoValidationMvcConfiguration);
+ var logger = Substitute.For>();
+
+ var actionFilter = new FluentValidationAutoValidationActionFilter(fluentValidationAutoValidationResultFactory, autoValidationMvcConfiguration, logger);
// Act
await actionFilter.OnActionExecutionAsync(actionExecutingContext, () => Task.FromResult(actionExecutedContext));