ASP.NET Core MediatRの処理にFluentValidationを組み込む
ASP.NET Core
でMedietR
を使用したプログラムにFluentValidation
を組み込み、バリデーション処理を追加します。
ASP.NET Coreに導入したMediatRのラッパークラスを作成するで作成したソースに対して、修正しています。
パッケージのインストール
以下の2つのパッケージをApplication
プロジェクトにインストールします。
Visual Studio
の場合は「NuGetパッケージの管理」からインストールできます。
VSCode
の場合は以下のURLからdotnet
コマンドなどを確認できます。
- FluentValidation.DependencyInjectionExtensions
- https://www.nuget.org/packages/FluentValidation.DependencyInjectionExtensions/10.4.0?_src=template
IPipelineBehaviorの作成
MediatR
にはハンドラ処理の前後に処理を組み込む仕組みがあります。
IPipelineBehavior
を継承したクラスを作成することにより、この仕組みを実現します。
以下のようなクラスを作成します。TRequest
には入力クラス、TResponse
には出力クラスが対応しています。
where
で入力クラスと出力クラスを指定することにより、特定のハンドラのみパイプライン処理を行うような動作が可能です。
今回はすべてのハンドラでバリデーションチェックを行いたいので、以下のように指定しています。
入力クラスはIRequest<TResponse>
としているため、実質すべての入力クラスが対象となります。(MediatR
を使用する場合は全ての入力クラスはIRequest
を継承しているため)
IResult
はASP.NET Coreに導入したMediatRのラッパークラスを作成するで定義したインターフェースです。全ての出力クラスはIResult
を継承するように作成していますので、こちらも実質全ての出力クラスが対象となります。
ValidationBehavior.cs
using System.Threading;
using FluentValidation;
using FluentValidation.Results;
using MediatR;
public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
where TResponse : IResult
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var context = new ValidationContext<TRequest>(request);
var failures = _validators
.Select(v => v.Validate(context))
.SelectMany(result => result.Errors)
.Where(f => f != null)
.ToList();
if (failures.Any())
{
throw new ValidationException(failures);
}
return await next();
}
}
作成したクラスをDIコンテナに登録します。
ASP.NET CoreにMediatRを導入するで作成したアプリケーション層のDependencyInjection.cs
に追記します。
DependencyInjection.cs
using System.Reflection;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using FluentValidation;
public static class DependencyInjection
{
public static IServiceCollection AddApplication(this IServiceCollection services)
{
services.AddMediatR(Assembly.GetExecutingAssembly());
services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
return services;
}
}
バリデーションチェックの作成
各ハンドラに対応するバリデーションチェックを作成します。
AbstractValidator<入力クラス>
を継承したクラスのコンストラクタでバリデーションルールを記載します。
using FluentValidation;
public class UserFindByIdQueryValidator : AbstractValidator<UserFindByIdQuery>
{
public UserFindByIdQueryValidator()
{
RuleFor(c => c.Id).NotEmpty().WithName("ユーザーID");
}
}
すべてのハンドラでバリデーションパイプラインを通過するように実装しましたが、ハンドラに対応するバリデーションチェックが実装されていない場合でも、例外は発生しません。(バリデーションチェックが不要なハンドラはバリデーションルールのクラスは作成不要)
以上で、バリデーションチェックの導入ができました。