Skip to content

Commit f2ad59a

Browse files
committed
add logging
1 parent ddc5345 commit f2ad59a

12 files changed

Lines changed: 95 additions & 78 deletions

File tree

FluentValidation.AutoValidation.Endpoints/src/Extensions/ServiceCollectionExtensions.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ public static class ServiceCollectionExtensions
1414
/// <param name="serviceCollection">The service collection.</param>
1515
/// <param name="autoValidationEndpointsConfiguration">The configuration delegate used to configure the FluentValidation AutoValidation Endpoints validation.</param>
1616
/// <returns>The service collection.</returns>
17-
public static IServiceCollection AddFluentValidationAutoValidation(this IServiceCollection serviceCollection,
18-
Action<AutoValidationEndpointsConfiguration>? autoValidationEndpointsConfiguration = null)
17+
public static IServiceCollection AddFluentValidationAutoValidation(this IServiceCollection serviceCollection, Action<AutoValidationEndpointsConfiguration>? autoValidationEndpointsConfiguration = null)
1918
{
2019
var configuration = new AutoValidationEndpointsConfiguration();
2120

FluentValidation.AutoValidation.Endpoints/src/Filters/FluentValidationAutoValidationEndpointFilter.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
using FluentValidation;
33
using Microsoft.AspNetCore.Http;
44
using Microsoft.Extensions.DependencyInjection;
5+
using Microsoft.Extensions.Logging;
56
using SharpGrip.FluentValidation.AutoValidation.Endpoints.Interceptors;
67
using SharpGrip.FluentValidation.AutoValidation.Endpoints.Results;
78
using SharpGrip.FluentValidation.AutoValidation.Shared.Extensions;
89

910
namespace SharpGrip.FluentValidation.AutoValidation.Endpoints.Filters
1011
{
11-
public class FluentValidationAutoValidationEndpointFilter : IEndpointFilter
12+
public class FluentValidationAutoValidationEndpointFilter(ILogger<FluentValidationAutoValidationEndpointFilter> logger) : IEndpointFilter
1213
{
1314
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext endpointFilterInvocationContext, EndpointFilterDelegate next)
1415
{
@@ -18,44 +19,60 @@ public class FluentValidationAutoValidationEndpointFilter : IEndpointFilter
1819
{
1920
if (argument != null && argument.GetType().IsCustomType() && serviceProvider.GetValidator(argument.GetType()) is IValidator validator)
2021
{
22+
logger.LogDebug("Starting validation for argument of type '{Type}'.", argument.GetType().Name);
23+
2124
var validatorInterceptor = validator as IValidatorInterceptor;
2225
var globalValidationInterceptor = serviceProvider.GetService<IGlobalValidationInterceptor>();
2326

2427
IValidationContext validationContext = new ValidationContext<object>(argument);
2528

2629
if (validatorInterceptor != null)
2730
{
31+
logger.LogDebug("Invoking validator interceptor BeforeValidation for argument '{Argument}'.", argument.GetType().Name);
2832
validationContext = await validatorInterceptor.BeforeValidation(endpointFilterInvocationContext, validationContext, endpointFilterInvocationContext.HttpContext.RequestAborted) ?? validationContext;
2933
}
3034

3135
if (globalValidationInterceptor != null)
3236
{
37+
logger.LogDebug("Invoking global validation interceptor BeforeValidation for argument '{Argument}'.", argument.GetType().Name);
3338
validationContext = await globalValidationInterceptor.BeforeValidation(endpointFilterInvocationContext, validationContext, endpointFilterInvocationContext.HttpContext.RequestAborted) ?? validationContext;
3439
}
3540

3641
var validationResult = await validator.ValidateAsync(validationContext, endpointFilterInvocationContext.HttpContext.RequestAborted);
3742

3843
if (validatorInterceptor != null)
3944
{
45+
logger.LogDebug("Invoking validator interceptor AfterValidation for argument '{Argument}'.", argument.GetType().Name);
4046
validationResult = await validatorInterceptor.AfterValidation(endpointFilterInvocationContext, validationContext, validationResult, endpointFilterInvocationContext.HttpContext.RequestAborted) ?? validationResult;
4147
}
4248

4349
if (globalValidationInterceptor != null)
4450
{
51+
logger.LogDebug("Invoking global validation interceptor AfterValidation for argument '{Argument}'.", argument.GetType().Name);
4552
validationResult = await globalValidationInterceptor.AfterValidation(endpointFilterInvocationContext, validationContext, validationResult, endpointFilterInvocationContext.HttpContext.RequestAborted) ?? validationResult;
4653
}
4754

4855
if (!validationResult.IsValid)
4956
{
57+
logger.LogDebug("Validation result not valid for argument '{Argument}': {ErrorCount} validation errors found.", argument.GetType().Name, validationResult.Errors.Count);
58+
5059
var fluentValidationAutoValidationResultFactory = serviceProvider.GetService<IFluentValidationAutoValidationResultFactory>();
5160

61+
logger.LogDebug("Creating result for path '{Path}'.", endpointFilterInvocationContext.HttpContext.Request.Path);
62+
5263
if (fluentValidationAutoValidationResultFactory != null)
5364
{
65+
logger.LogTrace("Creating result for path '{Path}' using a custom result factory.", endpointFilterInvocationContext.HttpContext.Request.Path);
66+
5467
return fluentValidationAutoValidationResultFactory.CreateResult(endpointFilterInvocationContext, validationResult);
5568
}
5669

70+
logger.LogTrace("Creating result for path '{Path}' using the default result factory.", endpointFilterInvocationContext.HttpContext.Request.Path);
71+
5772
return new FluentValidationAutoValidationDefaultResultFactory().CreateResult(endpointFilterInvocationContext, validationResult);
5873
}
74+
75+
logger.LogDebug("Validation result valid for argument '{Argument}'.", argument.GetType().Name);
5976
}
6077
}
6178

FluentValidation.AutoValidation.Mvc/src/Attributes/AutoValidateAlwaysAttribute.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,5 @@
33
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Attributes
44
{
55
[AttributeUsage(AttributeTargets.Parameter)]
6-
public class AutoValidateAlwaysAttribute : Attribute
7-
{
8-
}
6+
public class AutoValidateAlwaysAttribute : Attribute;
97
}

FluentValidation.AutoValidation.Mvc/src/Attributes/AutoValidateNeverAttribute.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,5 @@
33
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Attributes
44
{
55
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Parameter)]
6-
public class AutoValidateNeverAttribute : Attribute
7-
{
8-
}
6+
public class AutoValidateNeverAttribute : Attribute;
97
}

FluentValidation.AutoValidation.Mvc/src/Attributes/AutoValidationAttribute.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,5 @@
33
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Attributes
44
{
55
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
6-
public class AutoValidationAttribute : Attribute
7-
{
8-
}
6+
public class AutoValidationAttribute : Attribute;
97
}

FluentValidation.AutoValidation.Mvc/src/Extensions/ServiceCollectionExtensions.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,7 @@ public static IServiceCollection AddFluentValidationAutoValidation(this IService
3434

3535
if (configuration.DisableBuiltInModelValidation)
3636
{
37-
serviceCollection.AddSingleton<IObjectModelValidator, FluentValidationAutoValidationObjectModelValidator>(serviceProvider =>
38-
new FluentValidationAutoValidationObjectModelValidator(
39-
serviceProvider.GetRequiredService<IModelMetadataProvider>(),
40-
serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Value.ModelValidatorProviders,
41-
configuration.DisableBuiltInModelValidation));
37+
serviceCollection.AddSingleton<IObjectModelValidator, FluentValidationAutoValidationObjectModelValidator>(serviceProvider => new FluentValidationAutoValidationObjectModelValidator(serviceProvider.GetRequiredService<IModelMetadataProvider>(), serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Value.ModelValidatorProviders, configuration.DisableBuiltInModelValidation));
4238
}
4339

4440
// Add the default result factory.

FluentValidation.AutoValidation.Mvc/src/Filters/FluentValidationAutoValidationActionFilter.cs

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Microsoft.AspNetCore.Mvc.Infrastructure;
1212
using Microsoft.AspNetCore.Mvc.ModelBinding;
1313
using Microsoft.Extensions.DependencyInjection;
14+
using Microsoft.Extensions.Logging;
1415
using Microsoft.Extensions.Options;
1516
using SharpGrip.FluentValidation.AutoValidation.Mvc.Attributes;
1617
using SharpGrip.FluentValidation.AutoValidation.Mvc.Configuration;
@@ -21,27 +22,25 @@
2122

2223
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Filters
2324
{
24-
public class FluentValidationAutoValidationActionFilter : IAsyncActionFilter
25+
public class FluentValidationAutoValidationActionFilter(IFluentValidationAutoValidationResultFactory fluentValidationAutoValidationResultFactory, IOptions<AutoValidationMvcConfiguration> autoValidationMvcConfiguration, ILogger<FluentValidationAutoValidationActionFilter> logger) : IAsyncActionFilter
2526
{
26-
private readonly IFluentValidationAutoValidationResultFactory fluentValidationAutoValidationResultFactory;
27-
private readonly AutoValidationMvcConfiguration autoValidationMvcConfiguration;
28-
29-
public FluentValidationAutoValidationActionFilter(IFluentValidationAutoValidationResultFactory fluentValidationAutoValidationResultFactory, IOptions<AutoValidationMvcConfiguration> autoValidationMvcConfiguration)
30-
{
31-
this.fluentValidationAutoValidationResultFactory = fluentValidationAutoValidationResultFactory;
32-
this.autoValidationMvcConfiguration = autoValidationMvcConfiguration.Value;
33-
}
27+
private readonly AutoValidationMvcConfiguration autoValidationMvcConfiguration = autoValidationMvcConfiguration.Value;
3428

3529
public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutingContext, ActionExecutionDelegate next)
3630
{
31+
var controllerActionDescriptor = (ControllerActionDescriptor) actionExecutingContext.ActionDescriptor;
32+
33+
logger.LogDebug("Starting validation for action '{Action}' on controller '{Controller}'.", controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
34+
3735
if (IsValidController(actionExecutingContext.Controller))
3836
{
3937
var endpoint = actionExecutingContext.HttpContext.GetEndpoint();
40-
var controllerActionDescriptor = (ControllerActionDescriptor) actionExecutingContext.ActionDescriptor;
4138
var serviceProvider = actionExecutingContext.HttpContext.RequestServices;
4239

4340
if (endpoint != null && ((autoValidationMvcConfiguration.ValidationStrategy == ValidationStrategy.Annotations && !endpoint.Metadata.OfType<AutoValidationAttribute>().Any()) || endpoint.Metadata.OfType<AutoValidateNeverAttribute>().Any()))
4441
{
42+
logger.LogDebug("Skipping validation for action '{Action}' on controller '{Controller}' due to validation strategy or AutoValidateNeverAttribute.", controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
43+
4544
HandleUnvalidatedEntries(actionExecutingContext);
4645

4746
await next();
@@ -64,6 +63,8 @@ public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutingC
6463

6564
if (subject != null && parameterType != null && parameterType.IsCustomType() && !hasAutoValidateNeverAttribute && (hasAutoValidateAlwaysAttribute || HasValidBindingSource(bindingSource)) && serviceProvider.GetValidator(parameterType) is IValidator validator)
6665
{
66+
logger.LogDebug("Validating parameter '{Parameter}' of type '{Type}' for action '{Action}' on controller '{Controller}'.", parameter.Name, parameterType.Name, controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
67+
6768
// ReSharper disable once SuspiciousTypeConversion.Global
6869
var validatorInterceptor = validator as IValidatorInterceptor;
6970
var globalValidationInterceptor = serviceProvider.GetService<IGlobalValidationInterceptor>();
@@ -72,11 +73,13 @@ public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutingC
7273

7374
if (validatorInterceptor != null)
7475
{
76+
logger.LogDebug("Invoking validator interceptor BeforeValidation for parameter '{Parameter}'.", parameter.Name);
7577
validationContext = await validatorInterceptor.BeforeValidation(actionExecutingContext, validationContext) ?? validationContext;
7678
}
7779

7880
if (globalValidationInterceptor != null)
7981
{
82+
logger.LogDebug("Invoking global validation interceptor BeforeValidation for parameter '{Parameter}'.", parameter.Name);
8083
validationContext = await globalValidationInterceptor.BeforeValidation(actionExecutingContext, validationContext) ?? validationContext;
8184
}
8285

@@ -85,21 +88,31 @@ public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutingC
8588

8689
if (validatorInterceptor != null)
8790
{
91+
logger.LogDebug("Invoking validator interceptor AfterValidation for parameter '{Parameter}'.", parameter.Name);
8892
validationResult = await validatorInterceptor.AfterValidation(actionExecutingContext, validationContext, validationResult) ?? validationResult;
8993
}
9094

9195
if (globalValidationInterceptor != null)
9296
{
97+
logger.LogDebug("Invoking global validation interceptor AfterValidation for parameter '{Parameter}'.", parameter.Name);
9398
validationResult = await globalValidationInterceptor.AfterValidation(actionExecutingContext, validationContext, validationResult) ?? validationResult;
9499
}
95100

96101
if (!validationResult.IsValid)
97102
{
103+
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);
104+
98105
foreach (var error in validationResult.Errors)
99106
{
107+
logger.LogTrace("Adding validation error '{ErrorMessage}' for '{ParameterName}' to ModelState.", error.ErrorMessage, parameter.Name);
108+
100109
actionExecutingContext.ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
101110
}
102111
}
112+
else
113+
{
114+
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);
115+
}
103116
}
104117
}
105118
}
@@ -108,13 +121,28 @@ public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutingC
108121

109122
if (!actionExecutingContext.ModelState.IsValid)
110123
{
124+
logger.LogDebug("ModelState is not valid for action '{Action}' on controller '{Controller}'. Creating validation problem details.", controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
125+
111126
var problemDetailsFactory = serviceProvider.GetRequiredService<ProblemDetailsFactory>();
112127
var validationProblemDetails = problemDetailsFactory.CreateValidationProblemDetails(actionExecutingContext.HttpContext, actionExecutingContext.ModelState);
113128

129+
logger.LogTrace("Creating action result for action '{Action}' on controller '{Controller}'.", controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
130+
114131
actionExecutingContext.Result = await fluentValidationAutoValidationResultFactory.CreateActionResult(actionExecutingContext, validationProblemDetails, validationResults);
115132

133+
if (actionExecutingContext.Result != null)
134+
{
135+
logger.LogTrace("Action result created for action '{Action}' on controller '{Controller}'.", controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
136+
}
137+
else
138+
{
139+
logger.LogTrace("No action result created for action '{Action}' on controller '{Controller}'.", controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
140+
}
141+
116142
return;
117143
}
144+
145+
logger.LogDebug("ModelState is valid for action '{Action}' on controller '{Controller}'. Proceeding with action execution.", controllerActionDescriptor.ActionName, controllerActionDescriptor.ControllerName);
118146
}
119147

120148
await next();
@@ -126,6 +154,8 @@ private bool IsValidController(object controller)
126154

127155
if (controllerType.HasCustomAttribute<NonControllerAttribute>())
128156
{
157+
logger.LogDebug("Controller '{Controller}' is marked with NonControllerAttribute. Skipping validation.", controllerType.Name);
158+
129159
return false;
130160
}
131161

@@ -147,11 +177,17 @@ private void HandleUnvalidatedEntries(ActionExecutingContext context)
147177
{
148178
if (autoValidationMvcConfiguration.DisableBuiltInModelValidation)
149179
{
180+
logger.LogDebug("Skipping validation of unvalidated entries due to DisableBuiltInModelValidation being set to true.");
181+
150182
foreach (var modelStateEntry in context.ModelState.Values.Where(modelStateEntry => modelStateEntry.ValidationState == ModelValidationState.Unvalidated))
151183
{
152184
modelStateEntry.ValidationState = ModelValidationState.Skipped;
153185
}
154186
}
187+
else
188+
{
189+
logger.LogDebug("Skipping validation of unvalidated entries due to DisableBuiltInModelValidation being set to false.");
190+
}
155191
}
156192
}
157193
}

0 commit comments

Comments
 (0)