Skip to content

Commit 7d11445

Browse files
committed
FluentValidation nuget change from ASPNETCORE to regular one. This might brake some code
1 parent d61145d commit 7d11445

File tree

6 files changed

+97
-19
lines changed

6 files changed

+97
-19
lines changed

SharedKernel.Demo/Program.cs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using DistributedCache.Extensions;
2-
using DistributedCache.Options;
32
using FluentMinimalApiMapper;
4-
using Microsoft.AspNetCore.HttpLogging;
3+
using FluentValidation;
4+
using MediatR;
55
using Microsoft.AspNetCore.Mvc;
66
using SharedKernel.Demo2;
77
using ResponseCrafter.Enums;
@@ -40,6 +40,7 @@
4040
.AddOutboundLoggingHandler()
4141
.AddHealthChecks();
4242

43+
4344
builder.Services.ConfigureHttpJsonOptions(options =>
4445
{
4546
options.SerializerOptions.PropertyNamingPolicy = null;
@@ -71,6 +72,11 @@
7172
.UseOpenApi()
7273
.MapControllers();
7374

75+
app.MapPost("user", async ([FromBody] UserCommand user, ISender sender) =>
76+
{
77+
await sender.Send(user);
78+
return Results.Ok();
79+
});
7480

7581
app.MapPost("/receive-file", ([FromForm] IFormFile file) => TypedResults.Ok())
7682
.DisableAntiforgery();
@@ -141,4 +147,38 @@ public enum AnimalType
141147
Cat,
142148
Fish
143149
}
150+
}
151+
152+
public record UserCommand(string Name, string Email) : ICommand<string>;
153+
154+
public class UserCommandHandler : ICommandHandler<UserCommand, string>
155+
{
156+
public Task<string> Handle(UserCommand request, CancellationToken cancellationToken)
157+
{
158+
return Task.FromResult($"User {request.Name} with email {request.Email} created successfully.");
159+
}
160+
}
161+
162+
public class User
163+
{
164+
public string Name { get; set; } = string.Empty;
165+
public string Email { get; set; } = string.Empty;
166+
}
167+
168+
public class UserValidator : AbstractValidator<UserCommand>
169+
{
170+
public UserValidator()
171+
{
172+
RuleFor(x => x.Name)
173+
.NotEmpty()
174+
.WithMessage("Name is required.")
175+
.MaximumLength(100)
176+
.WithMessage("Name cannot exceed 100 characters.");
177+
178+
RuleFor(x => x.Email)
179+
.NotEmpty()
180+
.WithMessage("Email is required.")
181+
.EmailAddress()
182+
.WithMessage("Invalid email format.");
183+
}
144184
}

src/SharedKernel/Logging/StartupLoggerExtensions.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System.Diagnostics;
22
using System.Globalization;
33
using Microsoft.AspNetCore.Builder;
4-
using SharedKernel.Extensions;
54

65
namespace SharedKernel.Logging;
76

src/SharedKernel/SharedKernel.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
<PackageReadmeFile>Readme.md</PackageReadmeFile>
99
<Authors>Pandatech</Authors>
1010
<Copyright>MIT</Copyright>
11-
<Version>1.4.2</Version>
11+
<Version>1.5.0</Version>
1212
<PackageId>Pandatech.SharedKernel</PackageId>
1313
<Title>Pandatech Shared Kernel Library</Title>
1414
<PackageTags>Pandatech, shared kernel, library, OpenAPI, Swagger, utilities, scalar</PackageTags>
1515
<Description>Pandatech.SharedKernel provides centralized configurations, utilities, and extensions for ASP.NET Core projects. For more information refere to readme.md document.</Description>
1616
<RepositoryUrl>https://github.com/PandaTechAM/be-lib-sharedkernel</RepositoryUrl>
17-
<PackageReleaseNotes>Logging restructure</PackageReleaseNotes>
17+
<PackageReleaseNotes>FluentValidation nuget change from ASPNETCORE to regular one. This might brake some code</PackageReleaseNotes>
1818
</PropertyGroup>
1919

2020
<ItemGroup>
@@ -31,7 +31,7 @@
3131
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
3232
<PackageReference Include="Elastic.CommonSchema.Serilog" Version="8.18.1" />
3333
<PackageReference Include="FluentDateTime" Version="3.0.0" />
34-
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.1" />
34+
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.0.0" />
3535
<PackageReference Include="HtmlSanitizer" Version="9.0.884" />
3636
<PackageReference Include="MediatR" Version="12.5.0" />
3737
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.5" />
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using FluentValidation;
2+
using MediatR;
3+
using ResponseCrafter.HttpExceptions;
4+
5+
namespace SharedKernel.ValidatorAndMediatR.Behaviors;
6+
7+
public sealed class ValidationBehavior<TRequest, TResponse>(
8+
IEnumerable<IValidator<TRequest>> validators
9+
) : IPipelineBehavior<TRequest, TResponse> where TRequest : notnull
10+
{
11+
private readonly IValidator<TRequest>[] _validators =
12+
validators as IValidator<TRequest>[] ?? validators.ToArray();
13+
14+
public async Task<TResponse> Handle(
15+
TRequest request,
16+
RequestHandlerDelegate<TResponse> next,
17+
CancellationToken cancellationToken
18+
)
19+
{
20+
if (_validators.Length == 0)
21+
return await next(cancellationToken);
22+
23+
var context = new ValidationContext<TRequest>(request);
24+
var failures = (await Task.WhenAll(
25+
_validators.Select(v => v.ValidateAsync(context, cancellationToken))
26+
))
27+
.SelectMany(r => r.Errors)
28+
.Where(e => e is not null)
29+
.ToList();
30+
31+
if (!failures.Any())
32+
return await next(cancellationToken);
33+
34+
var errorMap = failures
35+
.GroupBy(e => e.PropertyName)
36+
.ToDictionary(g => g.Key, g => g.Select(e => e.ErrorMessage).Distinct().First());
37+
38+
throw new BadRequestException(errorMap);
39+
}
40+
}

src/SharedKernel/ValidatorAndMediatR/MediatRExtension.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ public static class MediatrExtension
1212
public static WebApplicationBuilder AddMediatrWithBehaviors(this WebApplicationBuilder builder,
1313
Assembly[] assemblies)
1414
{
15-
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(assemblies));
16-
builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviorWithoutResponse<,>));
17-
builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviorWithResponse<,>));
18-
builder.Services.AddValidatorsFromAssemblies(assemblies, includeInternalTypes: true);
15+
builder.Services
16+
.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(assemblies))
17+
.AddValidatorsFromAssemblies(assemblies, includeInternalTypes: true)
18+
.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
19+
1920
return builder;
2021
}
2122
}

src/SharedKernel/ValidatorAndMediatR/Validators/JsonValidator.cs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
using System.Text.Json;
2-
using FluentValidation;
1+
using FluentValidation;
32
using FluentValidation.Validators;
3+
using SharedKernel.Helpers;
44

55
namespace SharedKernel.ValidatorAndMediatR.Validators;
66

@@ -15,16 +15,14 @@ public override bool IsValid(ValidationContext<T> context, string? value)
1515
return true;
1616
}
1717

18-
try
18+
var isJson = ValidationHelper.IsJson(value);
19+
if (isJson)
1920
{
20-
JsonDocument.Parse(value);
2121
return true;
2222
}
23-
catch (JsonException)
24-
{
25-
context.AddFailure("The input is not valid JSON.");
26-
return false;
27-
}
23+
24+
context.AddFailure("The input is not valid JSON.");
25+
return false;
2826
}
2927

3028
protected override string GetDefaultMessageTemplate(string errorCode)

0 commit comments

Comments
 (0)