Skip to content

add check on controller attribute #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System.Linq;
using System;
using System.Linq;
using System.Threading.Tasks;
using FluentValidation;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
Expand All @@ -22,17 +24,15 @@
private readonly IFluentValidationAutoValidationResultFactory fluentValidationAutoValidationResultFactory;
private readonly AutoValidationMvcConfiguration autoValidationMvcConfiguration;

public FluentValidationAutoValidationActionFilter(
IFluentValidationAutoValidationResultFactory fluentValidationAutoValidationResultFactory,
IOptions<AutoValidationMvcConfiguration> autoValidationMvcConfiguration)
public FluentValidationAutoValidationActionFilter(IFluentValidationAutoValidationResultFactory fluentValidationAutoValidationResultFactory, IOptions<AutoValidationMvcConfiguration> autoValidationMvcConfiguration)
{
this.fluentValidationAutoValidationResultFactory = fluentValidationAutoValidationResultFactory;
this.autoValidationMvcConfiguration = autoValidationMvcConfiguration.Value;
}

public async Task OnActionExecutionAsync(ActionExecutingContext actionExecutingContext, ActionExecutionDelegate next)

Check warning on line 33 in FluentValidation.AutoValidation.Mvc/src/Filters/FluentValidationAutoValidationActionFilter.cs

View workflow job for this annotation

GitHub Actions / build

Refactor this method to reduce its Cognitive Complexity from 50 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)

Check warning on line 33 in FluentValidation.AutoValidation.Mvc/src/Filters/FluentValidationAutoValidationActionFilter.cs

View workflow job for this annotation

GitHub Actions / build

Rename parameter 'actionExecutingContext' to 'context' to match the interface declaration. (https://rules.sonarsource.com/csharp/RSPEC-927)
{
if (actionExecutingContext.Controller is ControllerBase controllerBase)
if (IsValidController(actionExecutingContext.Controller))
{
var endpoint = actionExecutingContext.HttpContext.GetEndpoint();
var controllerActionDescriptor = (ControllerActionDescriptor) actionExecutingContext.ActionDescriptor;
Expand All @@ -40,7 +40,7 @@

if (endpoint != null &&
((autoValidationMvcConfiguration.ValidationStrategy == ValidationStrategy.Annotations &&
!endpoint.Metadata.OfType<FluentValidationAutoValidationAttribute>().Any() && !endpoint.Metadata.OfType<AutoValidationAttribute>().Any()) ||

Check warning on line 43 in FluentValidation.AutoValidation.Mvc/src/Filters/FluentValidationAutoValidationActionFilter.cs

View workflow job for this annotation

GitHub Actions / build

'FluentValidationAutoValidationAttribute' is obsolete: 'Attribute is obsolete and will be removed in v2. Use the

Check warning on line 43 in FluentValidation.AutoValidation.Mvc/src/Filters/FluentValidationAutoValidationActionFilter.cs

View workflow job for this annotation

GitHub Actions / build

'FluentValidationAutoValidationAttribute' is obsolete: 'Attribute is obsolete and will be removed in v2. Use the
endpoint.Metadata.OfType<AutoValidateNeverAttribute>().Any()))
{
HandleUnvalidatedEntries(actionExecutingContext);
Expand Down Expand Up @@ -108,7 +108,8 @@

if (!actionExecutingContext.ModelState.IsValid)
{
var validationProblemDetails = controllerBase.ProblemDetailsFactory.CreateValidationProblemDetails(actionExecutingContext.HttpContext, actionExecutingContext.ModelState);
var problemDetailsFactory = serviceProvider.GetRequiredService<ProblemDetailsFactory>();
var validationProblemDetails = problemDetailsFactory.CreateValidationProblemDetails(actionExecutingContext.HttpContext, actionExecutingContext.ModelState);

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

Expand All @@ -119,6 +120,21 @@
await next();
}

private bool IsValidController(object controller)

Check warning on line 123 in FluentValidation.AutoValidation.Mvc/src/Filters/FluentValidationAutoValidationActionFilter.cs

View workflow job for this annotation

GitHub Actions / build

Make 'IsValidController' a static method. (https://rules.sonarsource.com/csharp/RSPEC-2325)

Check warning on line 123 in FluentValidation.AutoValidation.Mvc/src/Filters/FluentValidationAutoValidationActionFilter.cs

View workflow job for this annotation

GitHub Actions / build

Make 'IsValidController' a static method. (https://rules.sonarsource.com/csharp/RSPEC-2325)
{
var controllerType = controller.GetType();

if (controllerType.HasCustomAttribute<NonControllerAttribute>())
{
return false;
}

return controller is ControllerBase ||
controllerType.HasCustomAttribute<ControllerAttribute>() ||
controllerType.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) ||
controllerType.InheritsFromTypeWithNameEndingIn("Controller");
}

private bool HasValidBindingSource(BindingSource? bindingSource)
{
return (autoValidationMvcConfiguration.EnableBodyBindingSourceAutomaticValidation && bindingSource == BindingSource.Body) ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,20 @@ public static bool HasCustomAttribute<TAttribute>(this Type type) where TAttribu
{
return type.CustomAttributes.Any(attribute => attribute.AttributeType == typeof(TAttribute));
}

public static bool InheritsFromTypeWithNameEndingIn(this Type type, string name)
{
while (type.BaseType != null)
{
type = type.BaseType;

if (type.Name.EndsWith(name, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}

return false;
}
}
}
16 changes: 8 additions & 8 deletions Tests/FluentValidation.AutoValidation.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,26 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="NSubstitute.Analyzers.CSharp" Version="1.0.16">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="NSubstitute.Analyzers.CSharp" Version="1.0.17">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit" Version="2.6.4" />
<PackageReference Include="xunit.runner.console" Version="2.6.4">
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.console" Version="2.9.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.msbuild" Version="2.6.4">
<PackageReference Include="xunit.runner.msbuild" Version="2.9.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,11 @@ public async Task TestOnActionExecutionAsync()

serviceProvider.GetService(typeof(IValidator<>).MakeGenericType(typeof(TestModel))).Returns(new TestValidator());
serviceProvider.GetService(typeof(IGlobalValidationInterceptor)).Returns(new GlobalValidationInterceptor());
serviceProvider.GetService(typeof(ProblemDetailsFactory)).Returns(problemDetailsFactory);

problemDetailsFactory.CreateValidationProblemDetails(httpContext, modelStateDictionary).Returns(validationProblemDetails);
fluentValidationAutoValidationResultFactory.CreateActionResult(actionExecutingContext, validationProblemDetails).Returns(new BadRequestObjectResult(validationProblemDetails));
httpContext.RequestServices.Returns(serviceProvider);
controller.ProblemDetailsFactory = problemDetailsFactory;
actionExecutingContext.Controller.Returns(controller);
actionExecutingContext.ActionDescriptor = controllerActionDescriptor;
actionExecutingContext.ActionArguments.Returns(actionArguments);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Microsoft.AspNetCore.Mvc;
using SharpGrip.FluentValidation.AutoValidation.Mvc.Attributes;
using SharpGrip.FluentValidation.AutoValidation.Shared.Extensions;
using Xunit;
Expand Down Expand Up @@ -46,11 +47,38 @@ public void Test_HasCustomAttribute()
Assert.False(typeof(TestModelRecord).HasCustomAttribute<AutoValidationAttribute>());
}

[Fact]
public void Test_InheritsFromTypeWithNameEndingIn()
{
Assert.True(typeof(TestInherits1).InheritsFromTypeWithNameEndingIn("Controller"));
Assert.True(typeof(TestInherits1).InheritsFromTypeWithNameEndingIn("controller"));
Assert.True(typeof(TestInherits2).InheritsFromTypeWithNameEndingIn("Controller"));
Assert.True(typeof(TestInherits2).InheritsFromTypeWithNameEndingIn("controller"));
Assert.False(typeof(TestInherits3).InheritsFromTypeWithNameEndingIn("Controller"));
Assert.False(typeof(TestInherits3).InheritsFromTypeWithNameEndingIn("controller"));
Assert.False(typeof(TestInherits4).InheritsFromTypeWithNameEndingIn("Controller"));
Assert.False(typeof(TestInherits4).InheritsFromTypeWithNameEndingIn("controller"));
Assert.False(typeof(TestInherits5).InheritsFromTypeWithNameEndingIn("Controller"));
Assert.False(typeof(TestInherits5).InheritsFromTypeWithNameEndingIn("controller"));
}

[AutoValidation]
private class TestModelClass;

[AutoValidateNever]
private record TestModelRecord;

private enum TestModelEnum;

private class TestInherits1 : Controller;

private class TestInherits2 : CustomControllerBase;

private class TestInherits3 : ControllerBase;

private class TestInherits4 : ActionContext;

private class TestInherits5 : object;

private class CustomControllerBase : Controller;
}
Loading