Add validation pipeline with FluentValidation and MediatR
Introduced a validation pipeline using FluentValidation and MediatR to enhance request validation. Added the following: - Registered FluentValidation and MediatR dependencies in `ReC.Application.csproj`. - Updated `DependencyInjection.cs` to register validators and MediatR behaviors, including `ValidationBehavior<,>`. - Added `ValidationBehavior<TRequest, TResponse>` to handle request validation in the MediatR pipeline. - Created `ReadOutResQueryValidator` to enforce validation rules for `ReadOutResQuery`. - Refactored namespaces and imports for better organization. These changes improve extensibility, maintainability, and separation of concerns in the application.
This commit is contained in:
parent
b3ce5ad28a
commit
cd53d7fbae
30
src/ReC.Application/Common/Behaviors/ValidationBehavior.cs
Normal file
30
src/ReC.Application/Common/Behaviors/ValidationBehavior.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace ReC.Application.Common.Behaviors
|
||||||
|
{
|
||||||
|
public class ValidationBehavior<TRequest, TResponse>(IEnumerable<IValidator<TRequest>> validators) : IPipelineBehavior<TRequest, TResponse>
|
||||||
|
where TRequest : notnull
|
||||||
|
{
|
||||||
|
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
if (validators.Any())
|
||||||
|
{
|
||||||
|
var context = new ValidationContext<TRequest>(request);
|
||||||
|
|
||||||
|
var validationResults = await Task.WhenAll(
|
||||||
|
validators.Select(v =>
|
||||||
|
v.ValidateAsync(context, cancel)));
|
||||||
|
|
||||||
|
var failures = validationResults
|
||||||
|
.SelectMany(r => r.Errors)
|
||||||
|
.Where(f => f != null)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (failures.Count != 0)
|
||||||
|
throw new ValidationException(failures);
|
||||||
|
}
|
||||||
|
return await next(cancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,8 +1,10 @@
|
|||||||
using Microsoft.Extensions.Configuration;
|
using MediatR;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using ReC.Application.Common.Behaviors;
|
using ReC.Application.Common.Behaviors;
|
||||||
using ReC.Application.Common.Options;
|
using ReC.Application.Common.Options;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using FluentValidation;
|
||||||
|
|
||||||
namespace ReC.Application;
|
namespace ReC.Application;
|
||||||
|
|
||||||
@ -17,6 +19,8 @@ public static class DependencyInjection
|
|||||||
|
|
||||||
configOpt.ApplyConfigurations(services);
|
configOpt.ApplyConfigurations(services);
|
||||||
|
|
||||||
|
services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
|
||||||
|
|
||||||
services.AddAutoMapper(cfg =>
|
services.AddAutoMapper(cfg =>
|
||||||
{
|
{
|
||||||
cfg.AddMaps(Assembly.GetExecutingAssembly());
|
cfg.AddMaps(Assembly.GetExecutingAssembly());
|
||||||
@ -27,6 +31,7 @@ public static class DependencyInjection
|
|||||||
{
|
{
|
||||||
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
|
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
|
||||||
cfg.AddOpenBehaviors([typeof(BodyQueryBehavior<,>), typeof(HeaderQueryBehavior<,>)]);
|
cfg.AddOpenBehaviors([typeof(BodyQueryBehavior<,>), typeof(HeaderQueryBehavior<,>)]);
|
||||||
|
cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
|
||||||
cfg.LicenseKey = configOpt.LuckyPennySoftwareLicenseKey;
|
cfg.LicenseKey = configOpt.LuckyPennySoftwareLicenseKey;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
|
||||||
|
namespace ReC.Application.OutResults.Queries;
|
||||||
|
|
||||||
|
public class ReadOutResQueryValidator : AbstractValidator<ReadOutResQuery>
|
||||||
|
{
|
||||||
|
public ReadOutResQueryValidator()
|
||||||
|
{
|
||||||
|
RuleFor(x => x)
|
||||||
|
.Must(x => x.ActionId.HasValue || x.ProfileId.HasValue)
|
||||||
|
.WithMessage("At least one of ActionId or ProfileId must be provided.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,6 +11,8 @@
|
|||||||
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.5.0" />
|
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.5.0" />
|
||||||
<PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />
|
<PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />
|
||||||
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
|
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
|
||||||
|
<PackageReference Include="FluentValidation" Version="12.1.0" />
|
||||||
|
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.1.0" />
|
||||||
<PackageReference Include="MediatR" Version="13.1.0" />
|
<PackageReference Include="MediatR" Version="13.1.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.11" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.11" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.11" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.11" />
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user