Skip to content

Commit af92c92

Browse files
authored
Merge pull request #40 from SharpGrip/35-improve-mvc-endpoint-detection
add check on controller attribute
2 parents f4dc05a + f97434d commit af92c92

File tree

5 files changed

+75
-15
lines changed

5 files changed

+75
-15
lines changed

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

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
using System.Linq;
1+
using System;
2+
using System.Linq;
23
using System.Threading.Tasks;
34
using FluentValidation;
45
using Microsoft.AspNetCore.Http;
56
using Microsoft.AspNetCore.Mvc;
67
using Microsoft.AspNetCore.Mvc.Controllers;
78
using Microsoft.AspNetCore.Mvc.Filters;
9+
using Microsoft.AspNetCore.Mvc.Infrastructure;
810
using Microsoft.AspNetCore.Mvc.ModelBinding;
911
using Microsoft.Extensions.DependencyInjection;
1012
using Microsoft.Extensions.Options;
@@ -22,17 +24,15 @@ public class FluentValidationAutoValidationActionFilter : IAsyncActionFilter
2224
private readonly IFluentValidationAutoValidationResultFactory fluentValidationAutoValidationResultFactory;
2325
private readonly AutoValidationMvcConfiguration autoValidationMvcConfiguration;
2426

25-
public FluentValidationAutoValidationActionFilter(
26-
IFluentValidationAutoValidationResultFactory fluentValidationAutoValidationResultFactory,
27-
IOptions<AutoValidationMvcConfiguration> autoValidationMvcConfiguration)
27+
public FluentValidationAutoValidationActionFilter(IFluentValidationAutoValidationResultFactory fluentValidationAutoValidationResultFactory, IOptions<AutoValidationMvcConfiguration> autoValidationMvcConfiguration)
2828
{
2929
this.fluentValidationAutoValidationResultFactory = fluentValidationAutoValidationResultFactory;
3030
this.autoValidationMvcConfiguration = autoValidationMvcConfiguration.Value;
3131
}
3232

3333
public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutingContext, ActionExecutionDelegate next)
3434
{
35-
if (actionExecutingContext.Controller is ControllerBase controllerBase)
35+
if (IsValidController(actionExecutingContext.Controller))
3636
{
3737
var endpoint = actionExecutingContext.HttpContext.GetEndpoint();
3838
var controllerActionDescriptor = (ControllerActionDescriptor) actionExecutingContext.ActionDescriptor;
@@ -108,7 +108,8 @@ public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutingC
108108

109109
if (!actionExecutingContext.ModelState.IsValid)
110110
{
111-
var validationProblemDetails = controllerBase.ProblemDetailsFactory.CreateValidationProblemDetails(actionExecutingContext.HttpContext, actionExecutingContext.ModelState);
111+
var problemDetailsFactory = serviceProvider.GetRequiredService<ProblemDetailsFactory>();
112+
var validationProblemDetails = problemDetailsFactory.CreateValidationProblemDetails(actionExecutingContext.HttpContext, actionExecutingContext.ModelState);
112113

113114
actionExecutingContext.Result = fluentValidationAutoValidationResultFactory.CreateActionResult(actionExecutingContext, validationProblemDetails);
114115

@@ -119,6 +120,21 @@ public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutingC
119120
await next();
120121
}
121122

123+
private bool IsValidController(object controller)
124+
{
125+
var controllerType = controller.GetType();
126+
127+
if (controllerType.HasCustomAttribute<NonControllerAttribute>())
128+
{
129+
return false;
130+
}
131+
132+
return controller is ControllerBase ||
133+
controllerType.HasCustomAttribute<ControllerAttribute>() ||
134+
controllerType.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) ||
135+
controllerType.InheritsFromTypeWithNameEndingIn("Controller");
136+
}
137+
122138
private bool HasValidBindingSource(BindingSource? bindingSource)
123139
{
124140
return (autoValidationMvcConfiguration.EnableBodyBindingSourceAutomaticValidation && bindingSource == BindingSource.Body) ||

FluentValidation.AutoValidation.Shared/src/Extensions/TypeExtensions.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,20 @@ public static bool HasCustomAttribute<TAttribute>(this Type type) where TAttribu
2525
{
2626
return type.CustomAttributes.Any(attribute => attribute.AttributeType == typeof(TAttribute));
2727
}
28+
29+
public static bool InheritsFromTypeWithNameEndingIn(this Type type, string name)
30+
{
31+
while (type.BaseType != null)
32+
{
33+
type = type.BaseType;
34+
35+
if (type.Name.EndsWith(name, StringComparison.OrdinalIgnoreCase))
36+
{
37+
return true;
38+
}
39+
}
40+
41+
return false;
42+
}
2843
}
2944
}

Tests/FluentValidation.AutoValidation.Tests.csproj

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,26 @@
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
12-
<PackageReference Include="NSubstitute" Version="5.1.0" />
13-
<PackageReference Include="NSubstitute.Analyzers.CSharp" Version="1.0.16">
11+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
12+
<PackageReference Include="NSubstitute" Version="5.3.0" />
13+
<PackageReference Include="NSubstitute.Analyzers.CSharp" Version="1.0.17">
1414
<PrivateAssets>all</PrivateAssets>
1515
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1616
</PackageReference>
17-
<PackageReference Include="xunit" Version="2.6.4" />
18-
<PackageReference Include="xunit.runner.console" Version="2.6.4">
17+
<PackageReference Include="xunit" Version="2.9.2" />
18+
<PackageReference Include="xunit.runner.console" Version="2.9.2">
1919
<PrivateAssets>all</PrivateAssets>
2020
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2121
</PackageReference>
22-
<PackageReference Include="xunit.runner.msbuild" Version="2.6.4">
22+
<PackageReference Include="xunit.runner.msbuild" Version="2.9.2">
2323
<PrivateAssets>all</PrivateAssets>
2424
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2525
</PackageReference>
26-
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
26+
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
2727
<PrivateAssets>all</PrivateAssets>
2828
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2929
</PackageReference>
30-
<PackageReference Include="coverlet.collector" Version="6.0.0">
30+
<PackageReference Include="coverlet.collector" Version="6.0.2">
3131
<PrivateAssets>all</PrivateAssets>
3232
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3333
</PackageReference>

Tests/src/FluentValidation.AutoValidation.Mvc/Filters/FluentValidationAutoValidationActionFilterTest.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,11 @@ public async Task TestOnActionExecutionAsync()
7373

7474
serviceProvider.GetService(typeof(IValidator<>).MakeGenericType(typeof(TestModel))).Returns(new TestValidator());
7575
serviceProvider.GetService(typeof(IGlobalValidationInterceptor)).Returns(new GlobalValidationInterceptor());
76+
serviceProvider.GetService(typeof(ProblemDetailsFactory)).Returns(problemDetailsFactory);
77+
7678
problemDetailsFactory.CreateValidationProblemDetails(httpContext, modelStateDictionary).Returns(validationProblemDetails);
7779
fluentValidationAutoValidationResultFactory.CreateActionResult(actionExecutingContext, validationProblemDetails).Returns(new BadRequestObjectResult(validationProblemDetails));
7880
httpContext.RequestServices.Returns(serviceProvider);
79-
controller.ProblemDetailsFactory = problemDetailsFactory;
8081
actionExecutingContext.Controller.Returns(controller);
8182
actionExecutingContext.ActionDescriptor = controllerActionDescriptor;
8283
actionExecutingContext.ActionArguments.Returns(actionArguments);

Tests/src/FluentValidation.AutoValidation.Shared/Extensions/TypeExtensionsTest.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using Microsoft.AspNetCore.Mvc;
23
using SharpGrip.FluentValidation.AutoValidation.Mvc.Attributes;
34
using SharpGrip.FluentValidation.AutoValidation.Shared.Extensions;
45
using Xunit;
@@ -46,11 +47,38 @@ public void Test_HasCustomAttribute()
4647
Assert.False(typeof(TestModelRecord).HasCustomAttribute<AutoValidationAttribute>());
4748
}
4849

50+
[Fact]
51+
public void Test_InheritsFromTypeWithNameEndingIn()
52+
{
53+
Assert.True(typeof(TestInherits1).InheritsFromTypeWithNameEndingIn("Controller"));
54+
Assert.True(typeof(TestInherits1).InheritsFromTypeWithNameEndingIn("controller"));
55+
Assert.True(typeof(TestInherits2).InheritsFromTypeWithNameEndingIn("Controller"));
56+
Assert.True(typeof(TestInherits2).InheritsFromTypeWithNameEndingIn("controller"));
57+
Assert.False(typeof(TestInherits3).InheritsFromTypeWithNameEndingIn("Controller"));
58+
Assert.False(typeof(TestInherits3).InheritsFromTypeWithNameEndingIn("controller"));
59+
Assert.False(typeof(TestInherits4).InheritsFromTypeWithNameEndingIn("Controller"));
60+
Assert.False(typeof(TestInherits4).InheritsFromTypeWithNameEndingIn("controller"));
61+
Assert.False(typeof(TestInherits5).InheritsFromTypeWithNameEndingIn("Controller"));
62+
Assert.False(typeof(TestInherits5).InheritsFromTypeWithNameEndingIn("controller"));
63+
}
64+
4965
[AutoValidation]
5066
private class TestModelClass;
5167

5268
[AutoValidateNever]
5369
private record TestModelRecord;
5470

5571
private enum TestModelEnum;
72+
73+
private class TestInherits1 : Controller;
74+
75+
private class TestInherits2 : CustomControllerBase;
76+
77+
private class TestInherits3 : ControllerBase;
78+
79+
private class TestInherits4 : ActionContext;
80+
81+
private class TestInherits5 : object;
82+
83+
private class CustomControllerBase : Controller;
5684
}

0 commit comments

Comments
 (0)