Skip to content

Commit 5654e44

Browse files
committed
Consolidated duplicate code
1 parent cb83c00 commit 5654e44

7 files changed

+112
-65
lines changed

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

-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ public static IServiceCollection AddFluentValidationAutoValidation(this IService
3434

3535
serviceCollection.AddSingleton<IObjectModelValidator, FluentValidationAutoValidationObjectModelValidator>(serviceProvider =>
3636
new FluentValidationAutoValidationObjectModelValidator(
37-
serviceProvider,
3837
serviceProvider.GetRequiredService<IModelMetadataProvider>(),
3938
serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Value.ModelValidatorProviders,
4039
configuration.DisableBuiltInModelValidation));

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

+5-34
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
using SharpGrip.FluentValidation.AutoValidation.Mvc.Attributes;
1414
using SharpGrip.FluentValidation.AutoValidation.Mvc.Configuration;
1515
using SharpGrip.FluentValidation.AutoValidation.Mvc.Enums;
16-
using SharpGrip.FluentValidation.AutoValidation.Mvc.Interceptors;
1716
using SharpGrip.FluentValidation.AutoValidation.Mvc.Results;
17+
using SharpGrip.FluentValidation.AutoValidation.Mvc.Validation;
1818
using SharpGrip.FluentValidation.AutoValidation.Shared.Extensions;
1919

2020
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Filters
@@ -55,45 +55,16 @@ public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutingC
5555
if (actionExecutingContext.ActionArguments.TryGetValue(parameter.Name, out var subject))
5656
{
5757
var parameterInfo = (parameter as ControllerParameterDescriptor)?.ParameterInfo;
58-
var parameterType = subject?.GetType();
5958
var bindingSource = parameter.BindingInfo?.BindingSource;
6059

6160
var hasAutoValidateAlwaysAttribute = parameterInfo?.HasCustomAttribute<AutoValidateAlwaysAttribute>() ?? false;
6261
var hasAutoValidateNeverAttribute = parameterInfo?.HasCustomAttribute<AutoValidateNeverAttribute>() ?? false;
6362

64-
if (subject != null && parameterType != null && parameterType.IsCustomType() &&
65-
!hasAutoValidateNeverAttribute && (hasAutoValidateAlwaysAttribute || HasValidBindingSource(bindingSource)) &&
66-
serviceProvider.GetValidator(parameterType) is IValidator validator)
63+
if (!hasAutoValidateNeverAttribute && (hasAutoValidateAlwaysAttribute || HasValidBindingSource(bindingSource)))
6764
{
68-
// ReSharper disable once SuspiciousTypeConversion.Global
69-
var validatorInterceptor = validator as IValidatorInterceptor;
70-
var globalValidationInterceptor = serviceProvider.GetService<IGlobalValidationInterceptor>();
71-
72-
IValidationContext validationContext = new ValidationContext<object>(subject);
73-
74-
if (validatorInterceptor != null)
75-
{
76-
validationContext = validatorInterceptor.BeforeValidation(actionExecutingContext, validationContext) ?? validationContext;
77-
}
78-
79-
if (globalValidationInterceptor != null)
80-
{
81-
validationContext = globalValidationInterceptor.BeforeValidation(actionExecutingContext, validationContext) ?? validationContext;
82-
}
83-
84-
var validationResult = await validator.ValidateAsync(validationContext, actionExecutingContext.HttpContext.RequestAborted);
85-
86-
if (validatorInterceptor != null)
87-
{
88-
validationResult = validatorInterceptor.AfterValidation(actionExecutingContext, validationContext) ?? validationResult;
89-
}
90-
91-
if (globalValidationInterceptor != null)
92-
{
93-
validationResult = globalValidationInterceptor.AfterValidation(actionExecutingContext, validationContext) ?? validationResult;
94-
}
95-
96-
if (!validationResult.IsValid)
65+
var validationResult = await FluentValidationHelper.ValidateWithFluentValidationAsync(
66+
serviceProvider, subject, actionExecutingContext);
67+
if (validationResult != null && !validationResult.IsValid)
9768
{
9869
foreach (var error in validationResult.Errors)
9970
{

FluentValidation.AutoValidation.Mvc/src/Validation/FluentValidationAutoValidationObjectModelValidator.cs

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using Microsoft.AspNetCore.Mvc;
43
using Microsoft.AspNetCore.Mvc.ModelBinding;
54
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
@@ -8,27 +7,25 @@ namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Validation
87
{
98
public class FluentValidationAutoValidationObjectModelValidator : ObjectModelValidator
109
{
11-
private readonly IServiceProvider serviceProvider;
1210
private readonly bool disableBuiltInModelValidation;
1311

1412
public FluentValidationAutoValidationObjectModelValidator(
15-
IServiceProvider serviceProvider,
1613
IModelMetadataProvider modelMetadataProvider,
17-
IList<IModelValidatorProvider> validatorProviders, bool disableBuiltInModelValidation)
14+
IList<IModelValidatorProvider> validatorProviders,
15+
bool disableBuiltInModelValidation)
1816
: base(modelMetadataProvider, validatorProviders)
1917
{
20-
this.serviceProvider = serviceProvider;
2118
this.disableBuiltInModelValidation = disableBuiltInModelValidation;
2219
}
2320

24-
public override ValidationVisitor GetValidationVisitor(ActionContext actionContext,
21+
public override ValidationVisitor GetValidationVisitor(
22+
ActionContext actionContext,
2523
IModelValidatorProvider validatorProvider,
2624
ValidatorCache validatorCache,
2725
IModelMetadataProvider metadataProvider,
2826
ValidationStateDictionary? validationState)
2927
{
3028
return new FluentValidationAutoValidationValidationVisitor(
31-
serviceProvider,
3229
actionContext,
3330
validatorProvider,
3431
validatorCache,

FluentValidation.AutoValidation.Mvc/src/Validation/FluentValidationAutoValidationValidationVisitor.cs

+21-15
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
using System;
1+
using System.Collections.Generic;
22
using System.Linq;
3-
using FluentValidation;
3+
using System.Threading.Tasks;
44
using Microsoft.AspNetCore.Mvc;
5+
using Microsoft.AspNetCore.Mvc.Filters;
56
using Microsoft.AspNetCore.Mvc.ModelBinding;
67
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
78

89
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Validation
910
{
1011
public class FluentValidationAutoValidationValidationVisitor : ValidationVisitor
1112
{
12-
private readonly IServiceProvider serviceProvider;
13+
private readonly ActionContext actionContext;
1314
private readonly bool disableBuiltInModelValidation;
1415

1516
public FluentValidationAutoValidationValidationVisitor(
16-
IServiceProvider serviceProvider,
1717
ActionContext actionContext,
1818
IModelValidatorProvider validatorProvider,
1919
ValidatorCache validatorCache,
@@ -22,27 +22,27 @@ public FluentValidationAutoValidationValidationVisitor(
2222
bool disableBuiltInModelValidation)
2323
: base(actionContext, validatorProvider, validatorCache, metadataProvider, validationState)
2424
{
25-
this.serviceProvider = serviceProvider;
25+
this.actionContext = actionContext;
2626
this.disableBuiltInModelValidation = disableBuiltInModelValidation;
2727
}
2828

2929
public override bool Validate(ModelMetadata? metadata, string? key, object? model, bool alwaysValidateAtTopLevel)
3030
{
3131
// If built in model validation is disabled return true for later validation in the action filter.
3232
bool isBaseValid = disableBuiltInModelValidation || base.Validate(metadata, key, model, alwaysValidateAtTopLevel);
33-
return Validate(isBaseValid, key, model);
33+
return ValidateAsync(isBaseValid, key, model).Result;
3434
}
3535

3636
#if !NETCOREAPP3_1
3737
public override bool Validate(ModelMetadata? metadata, string? key, object? model, bool alwaysValidateAtTopLevel, object? container)
3838
{
3939
// If built in model validation is disabled return true for later validation in the action filter.
4040
bool isBaseValid = disableBuiltInModelValidation || base.Validate(metadata, key, model, alwaysValidateAtTopLevel, container);
41-
return Validate(isBaseValid, key, model);
41+
return ValidateAsync(isBaseValid, key, model).Result;
4242
}
4343
#endif
4444

45-
private bool Validate(
45+
private async Task<bool> ValidateAsync(
4646
bool isBaseValid,
4747
string? key,
4848
object? model)
@@ -52,21 +52,27 @@ private bool Validate(
5252
return isBaseValid;
5353
}
5454

55-
// Use FluentValidation to perform additional validation
56-
var validatorType = typeof(IValidator<>).MakeGenericType(model.GetType());
57-
if (!(this.serviceProvider.GetService(validatorType) is IValidator validator))
55+
var actionExecutingContext = new ActionExecutingContext(
56+
actionContext,
57+
new List<IFilterMetadata>(),
58+
new Dictionary<string, object>(),
59+
null);
60+
61+
var validationResult = await FluentValidationHelper.ValidateWithFluentValidationAsync(
62+
actionContext.HttpContext.RequestServices,
63+
model,
64+
actionExecutingContext);
65+
if (validationResult == null)
5866
{
5967
return isBaseValid;
6068
}
6169

62-
var validationResult = validator.Validate(new ValidationContext<object>(model));
6370
foreach (var error in validationResult.Errors)
6471
{
6572
var keyName = string.IsNullOrEmpty(key) ? error.PropertyName : $"{key}.{error.PropertyName}";
66-
67-
if (!ModelState[keyName]?.Errors.Any(e => e.ErrorMessage == error.ErrorMessage) ?? true)
73+
if (!this.ModelState[keyName]?.Errors.Any(e => e.ErrorMessage == error.ErrorMessage) ?? true)
6874
{
69-
ModelState.AddModelError(keyName, error.ErrorMessage);
75+
this.ModelState.AddModelError(keyName, error.ErrorMessage);
7076
}
7177
}
7278

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using FluentValidation;
4+
using FluentValidation.Results;
5+
using Microsoft.AspNetCore.Mvc.Filters;
6+
using Microsoft.Extensions.DependencyInjection;
7+
using SharpGrip.FluentValidation.AutoValidation.Mvc.Interceptors;
8+
using SharpGrip.FluentValidation.AutoValidation.Shared.Extensions;
9+
10+
namespace SharpGrip.FluentValidation.AutoValidation.Mvc.Validation
11+
{
12+
public static class FluentValidationHelper
13+
{
14+
public static async Task<ValidationResult?> ValidateWithFluentValidationAsync(
15+
IServiceProvider serviceProvider,
16+
object? model,
17+
ActionExecutingContext actionExecutingContext)
18+
{
19+
if (model == null)
20+
{
21+
return null;
22+
}
23+
24+
var modelType = model.GetType();
25+
if (modelType == null)
26+
{
27+
return null;
28+
}
29+
30+
if (!modelType.IsCustomType())
31+
{
32+
return null;
33+
}
34+
35+
var validator = serviceProvider.GetValidator(modelType) as IValidator;
36+
if (validator == null)
37+
{
38+
return null;
39+
}
40+
41+
IValidationContext validationContext = new ValidationContext<object>(model);
42+
43+
var validatorInterceptor = validator as IValidatorInterceptor;
44+
if (validatorInterceptor != null)
45+
{
46+
validationContext = validatorInterceptor.BeforeValidation(actionExecutingContext, validationContext) ?? validationContext;
47+
}
48+
49+
var globalValidationInterceptor = serviceProvider.GetService<IGlobalValidationInterceptor>();
50+
if (globalValidationInterceptor != null)
51+
{
52+
validationContext = globalValidationInterceptor.BeforeValidation(actionExecutingContext, validationContext) ?? validationContext;
53+
}
54+
55+
var validationResult = await validator.ValidateAsync(validationContext, actionExecutingContext.HttpContext.RequestAborted);
56+
57+
if (validatorInterceptor != null)
58+
{
59+
validationResult = validatorInterceptor.AfterValidation(actionExecutingContext, validationContext) ?? validationResult;
60+
}
61+
62+
if (globalValidationInterceptor != null)
63+
{
64+
validationResult = globalValidationInterceptor.AfterValidation(actionExecutingContext, validationContext) ?? validationResult;
65+
}
66+
67+
return validationResult;
68+
}
69+
}
70+
}

Tests/src/FluentValidation.AutoValidation.Mvc/Validation/FluentValidationAutoValidationObjectModelValidatorTest.cs

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using System.Linq;
43
using FluentValidation;
54
using Microsoft.AspNetCore.Http;
@@ -21,15 +20,14 @@ public class FluentValidationAutoValidationObjectModelValidatorTest
2120
[Fact]
2221
public void TestGetValidationVisitor()
2322
{
24-
var serviceProvider = Substitute.For<IServiceProvider>();
2523
var modelMetadataProvider = Substitute.For<IModelMetadataProvider>();
2624
var modelMetadataProviders = Substitute.For<IList<IModelValidatorProvider>>();
2725
var actionContext = Substitute.For<ActionContext>();
2826
var modelValidatorProvider = Substitute.For<IModelValidatorProvider>();
2927
var validatorCache = Substitute.For<ValidatorCache>();
3028

3129
var fluentValidationAutoValidationObjectModelValidator = new FluentValidationAutoValidationObjectModelValidator(
32-
serviceProvider, modelMetadataProvider, modelMetadataProviders, true);
30+
modelMetadataProvider, modelMetadataProviders, true);
3331

3432
Assert.IsType<FluentValidationAutoValidationValidationVisitor>(
3533
fluentValidationAutoValidationObjectModelValidator.GetValidationVisitor(actionContext, modelValidatorProvider, validatorCache, modelMetadataProvider, null));

Tests/src/FluentValidation.AutoValidation.Mvc/Validation/FluentValidationAutoValidationValidationVisitorTest.cs

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
using System;
1+
using Microsoft.AspNetCore.Http;
22
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.AspNetCore.Mvc.Abstractions;
34
using Microsoft.AspNetCore.Mvc.ModelBinding;
45
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
6+
using Microsoft.AspNetCore.Routing;
57
using NSubstitute;
68
using SharpGrip.FluentValidation.AutoValidation.Mvc.Validation;
79
using Xunit;
@@ -13,15 +15,19 @@ public class FluentValidationAutoValidationValidationVisitorTest
1315
[Fact]
1416
public void TestGetValidationVisitor()
1517
{
16-
var serviceProvider = Substitute.For<IServiceProvider>();
1718
var modelMetadataProvider = Substitute.For<IModelMetadataProvider>();
1819
var actionContext = Substitute.For<ActionContext>();
20+
var httpContext = Substitute.For<HttpContext>();
21+
var routeData = Substitute.For<RouteData>();
22+
var actionDescriptor = Substitute.For<ActionDescriptor>();
23+
actionContext.HttpContext = httpContext;
24+
actionContext.RouteData = routeData;
25+
actionContext.ActionDescriptor = actionDescriptor;
1926
var modelValidatorProvider = Substitute.For<IModelValidatorProvider>();
2027
var validatorCache = Substitute.For<ValidatorCache>();
2128

2229
var fluentValidationAutoValidationObjectModelValidator =
2330
new FluentValidationAutoValidationValidationVisitor(
24-
serviceProvider,
2531
actionContext,
2632
modelValidatorProvider,
2733
validatorCache,

0 commit comments

Comments
 (0)