.NET ベースの FluentValidation 検証チュートリアル

.NET ベースの FluentValidation 検証チュートリアル

FluentValidationは、.NETベースの検証フレームワークで、オープンソースかつ無料、そしてエレガントです。チェーン操作をサポートし、理解しやすく、機能が充実しています。さらに、MVC5、WebApi2、ASP.NET Coreと深く統合でき、コンポーネント内には十数種類の一般的なバリデーターが用意されており、拡張性が高く、カスタムバリデーターをサポートし、ローカライズ多言語にも対応しています。

最終更新 2024/01/19 5:41
零度
読了目安 18 分
カテゴリ
.NET
タグ
.NET C# ASP.NET Core オープンソース Web API

FluentValidation は、.NET ベースで開発された検証フレームワークで、オープンソースかつ無料で、エレガントで、チェーン操作をサポートし、理解しやすく、機能が充実しており、MVC5、WebApi2、ASP.NET CORE と深く統合できます。コンポーネント内には十数種類の一般的なバリデーターが用意されており、拡張性が高く、カスタムバリデーターをサポートし、ローカライズ多言語にも対応しています。

FluentValidation は非常に強力な検証フレームワークですが、中国語の資料は充実していません。零度(作者)は学習の過程で公式ドキュメントを翻訳し、この記事を作成しました。参考にしてください。

検証フレームワークを使用するには、プロジェクトに FluentValidation.dll への参照を追加する必要があります。netstandard2.0 ライブラリと .NET4.5 プラットフォームをサポートし、.NET Core プラットフォームもサポートします。最も簡単な方法は、NuGet パッケージマネージャーを使用してコンポーネントを参照することです。

Install-Package FluentValidation

ASP.NET Core で FluentValidation 拡張を使用するには、次のパッケージを参照します。

Install-Package FluentValidation.AspNetCore

ASP.NET MVC 5 または WebApi 2 プロジェクトに統合するには、それぞれ FluentValidation.Mvc5 および FluentValidation.WebApi パッケージを使用できます。

Install-Package FluentValidation.Mvc5
Install-Package FluentValidation.WebApi

最初の検証プログラムの作成

特定のオブジェクトに一連の検証ルールを定義するには、AbstractValidator<T> から継承するクラスを作成する必要があります。ここで、ジェネリックパラメータ T は検証するクラスの型です。顧客クラスがあるとします。

public class Customer {
  public int Id { get; set; }
  public string Surname { get; set; }
  public string Forename { get; set; }
  public decimal Discount { get; set; }
  public string Address { get; set; }
}

次に、AbstractValidator ジェネリッククラスから継承するバリデーターをカスタマイズし、コンストラクター内で LINQ 式を使用して RuleFor 検証ルールを記述します。

using FluentValidation;

public class CustomerValidator : AbstractValidator<Customer> {
  public CustomerValidator() {
    RuleFor(customer => customer.Surname).NotNull();
  }
}

検証を実行するには、定義された CustomerValidator バリデーターにエンティティクラス Customer を渡すだけです。

Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();

ValidationResult result = validator.Validate(customer);

この検証メソッドは、検証結果を表す ValidationResult オブジェクトを返します。ValidationResult には 2 つのプロパティがあります。IsValid プロパティはブール値で、検証が成功したかどうかを示し、Errors プロパティには検証失敗の詳細が含まれます。

次のコードは、検証失敗の詳細をコンソールに出力する例です。

Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();

ValidationResult results = validator.Validate(customer);

if(! results.IsValid) {
  foreach(var failure in results.Errors) {
    Console.WriteLine("Property " + failure.PropertyName + " Error was: " + failure.ErrorMessage);
  }
}

ルールのチェーン記述

同じプロパティに対して複数の検証をチェーンできます。次のコードは、Surname プロパティが null でなく、かつ "foo" 文字列と等しくないことを検証します。

using FluentValidation;

public class CustomerValidator : AbstractValidator<Customer> {
  public CustomerValidator() {
    RuleFor(customer => customer.Surname).NotNull().NotEqual("foo");
  }
}

例外のスロー

検証が失敗した場合、ValidationResult オブジェクトを返さずに、直接例外をスローしたい場合は、バリデーターの ValidateAndThrow メソッドを呼び出します。

Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();

validator.ValidateAndThrow(customer);

検証が失敗すると、ValidationException 型の例外がスローされ、上位プログラムでキャッチして詳細なエラー情報を取得できます。

コレクション

コレクションを検証する場合は、コレクション項目の型に対するルールを定義するだけです。次のルールは、コレクション内の各要素に対して NotNull チェックを実行します。

public class Person {
  public List<string> AddressLines {get;set;} = new List<string>();
}
public class PersonValidator : AbstractValidator<Person> {
  public PersonValidator() {
    RuleForEach(x => x.AddressLines).NotNull();
  }
}

複雑なプロパティ

バリデーターは複雑なプロパティにも使用できます。顧客クラスとアドレスクラスがあるとします。

public class Customer {
  public string Name { get; set; }
  public Address Address { get; set; }
}

public class Address {
  public string Line1 { get; set; }
  public string Line2 { get; set; }
  public string Town { get; set; }
  public string County { get; set; }
  public string Postcode { get; set; }
}

次に、アドレスベースの AddressValidator バリデーターを定義します。

public class AddressValidator : AbstractValidator<Address> {
  public AddressValidator() {
    RuleFor(address => address.Postcode).NotNull();
    //etc
  }
}

そして、顧客ベースの CustomerValidator バリデーターを定義し、アドレス検証にアドレスバリデーターを使用します。

public class CustomerValidator : AbstractValidator<Customer> {
  public CustomerValidator() {
    RuleFor(customer => customer.Name).NotNull();
    RuleFor(customer => customer.Address).SetValidator(new AddressValidator());
  }
}

また、コレクションプロパティにバリデーターを使用することもできます。顧客オブジェクトに注文コレクションプロパティがあるとします。

public class Customer {
   public IList<Order> Orders { get; set; }
}

public class Order {
  public string ProductName { get; set; }
  public decimal? Cost { get; set; }
}

var customer = new Customer();
customer.Orders = new List<Order> {
  new Order { ProductName = "Foo" },
  new Order { Cost = 5 }
};

OrderValidator バリデーターを定義します。

public class OrderValidator : AbstractValidator<Order> {
    public OrderValidator() {
        RuleFor(x => x.ProductName).NotNull();
        RuleFor(x => x.Cost).GreaterThan(0);
    }
}

このバリデーターは、CustomerValidator 内で SetCollectionValidator メソッドを使用して利用できます。

public class CustomerValidator : AbstractValidator<Customer> {
    public CustomerValidator() {
        RuleFor(x => x.Orders).SetCollectionValidator(new OrderValidator());
    }
}

var validator = new CustomerValidator();
var results = validator.Validate(customer);

検証を実行すると、検証結果から各注文プロパティの検証エラーの詳細を出力できます。

foreach(var result in results.Errors) {
   Console.WriteLine("Property name: " + result.PropertyName);
   Console.WriteLine("Error: " + result.ErrorMessage);
   Console.WriteLine("");
}
Property name: Orders[0].Cost
Error: 'Cost' must be greater than '0'.

Property name: Orders[1].ProductName
Error: 'Product Name' must not be empty.

検証ルールを記述する際、Where キーワードを使用して、検証対象外のオブジェクトを除外またはフィルターできます。

RuleFor(x => x.Orders).SetCollectionValidator(new OrderValidator())
        .Where(x => x.Cost != null);

ルールセットのサポート

ルールセットを使用すると、検証ルールをグループ化し、グループとしてまとめて実行し、他のルールを無視できます。Person クラスに Id、Surname、Forename の 3 つのプロパティがあるとします。Id を個別に検証し、Surname と Forename を Names ルールセットとしてグループ化します。

public class PersonValidator : AbstractValidator<Person> {
  public PersonValidator() {
     RuleSet("Names", () => {
        RuleFor(x => x.Surname).NotNull();
        RuleFor(x => x.Forename).NotNull();
     });

     RuleFor(x => x.Id).NotEqual(0);
  }
}

次に、バリデーターが提供する拡張メソッドを使用して、指定したルールセットに対して検証を実行できます。次のコードは Id プロパティを検証しません。

var validator = new PersonValidator();
var person = new Person();
var result = validator.Validate(person, ruleSet: "Names");

複数のルールセットを検証するには、カンマ区切りの文字列リストを使用します。

validator.Validate(person, ruleSet: "Names,MyRuleSet,SomeOtherRuleSet")

アスタリスク * を使用してすべてのルールを一致させ、ルールセットに関係なくすべてのルールを強制的に実行できます。

validator.Validate(person, ruleSet: "*")

構成方法

バリデーターの WithMessage メソッドを呼び出すことで、デフォルトの検証エラーメッセージを上書きできます。

RuleFor(customer => customer.Surname).NotNull().WithMessage("名前は必須です");

エラーメッセージでは、{PropertyName} プレースホルダーを使用してプロパティ名を置き換えることができます。

RuleFor(customer => customer.Surname).NotNull().WithMessage("{PropertyName}は必須です");

{PropertyName} の他に、フレームワークには {PropertyValue}{ComparisonValue}{MinLength}{MaxLength}{TotalLength} などのプレースホルダーが組み込まれています。詳細は公式ドキュメントを参照してください。

デフォルトでは、エラーメッセージにはプロパティ名が出力されます。次のルールでは、検証エラー時に "Surname" は必須ですと出力されます。

RuleFor(customer => customer.Surname).NotNull();

バリデーターは、WithName メソッドを使用してプロパティの別名を指定することもできます。次のコードでは "氏名" は必須ですと出力されます。

RuleFor(customer => customer.Surname).NotNull().WithName("氏名");

FluentValidation は、DisplayNameResolver を使用したカスタム別名取得ロジックもサポートしています。

ValidatorOptions.DisplayNameResolver = (type, member) => {
  if(member != null) {
     return member.Name + "Foo";
  }
  return null;
};

DisplayName 属性を使用してプロパティの別名を指定することもできます。

public class Person {
  [Display(Name="Last name")]
  public string Surname { get; set; }
}

特定の条件下では、When メソッドを使用して条件付きで検証ルールを設定できます。次のコードでは、GreaterThan ルールは IsPreferredCustomer が true の場合のみ実行されます。

RuleFor(customer => customer.CustomerDiscount).GreaterThan(0).When(customer => customer.IsPreferredCustomer);

複数のルールに同じ条件を指定する場合は、ルールの最後ではなく、メソッドのトップレベルで呼び出すことができます。

When(customer => customer.IsPreferred, () => {
   RuleFor(customer => customer.CustomerDiscount).GreaterThan(0);
   RuleFor(customer => customer.CreditCardNumber).NotNull();
});

デフォルトでは、複数のチェーン検証ルールを記述する場合、前のルールが失敗しても、後続のルールは実行されます。次のコードは、Surname が空でないことをチェックし、次に Surname が "零度" と等しくないことをチェックします。NotNull 検証が失敗しても、NotEqual 検証は呼び出されます。

RuleFor(x => x.Surname).NotNull().NotEqual("零度");

StopOnFirstFailure メソッドを使用してカスケードモードを設定できます。

RuleFor(x => x.Surname).Cascade(CascadeMode.StopOnFirstFailure).NotNull().NotEqual("foo");

これで、NotNull バリデーターが失敗した場合、NotEqual バリデーターは実行されません。

CascadeMode は列挙型で、Continue はルール定義内のすべてのバリデーターを常に呼び出すことを意味し、StopOnFirstFailure はバリデーターが失敗すると後続のルールの実行を停止します。

カスケードモードをグローバルに設定するには、アプリケーション起動時に ValidatorOptions 型の CascadeMode プロパティを変更します。

ValidatorOptions.CascadeMode = CascadeMode.StopOnFirstFailure;

単一のバリデータークラスにカスケードモードを設定するには、次のように記述します。

public class PersonValidator : AbstractValidator<Person> {
  public PersonValidator() {

    // First set the cascade mode
    CascadeMode = CascadeMode.StopOnFirstFailure;

    // Rule definitions follow
    RuleFor(...)
    RuleFor(...)

   }
}

デフォルトでは、FluentValidation のすべてのルールは独立して実行され、相互に影響しません。これは非同期検証のパフォーマンス向上に役立ちます。ただし、特定のルールが別のルールの検証完了後にのみ実行されるようにしたい場合もあります。DependentRules を使用してルールの依存関係を指定します。依存ルールの検証が完了すると、現在のルールが実行されます。

RuleFor(x => x.Surname).NotNull().DependentRules(() => {
  RuleFor(x => x.Forename).NotNull();
});

ValidationContext コンテキストの RootContextData ディクショナリを介して、バリデーターにデータを渡すことができます。

var instanceToValidate = new Person();
var context = new ValidationContext(person);
context.RootContextData["MyCustomData"] = "Test";
var validator = new PersonValidator();
validator.Validate(context);

次に、Custom メソッドを呼び出す任意のカスタムプロパティバリデーター内で RootContextData ディクショナリにアクセスできます。

RuleFor(x => x.Surname).Custom((x, context) => {
  if(context.ParentContext.RootContextData.ContainsKey("MyCustomData")) {
    context.AddFailure("My error message");
  }
});

バリデーター呼び出しのたびに特定のコードを実行する必要がある場合は、PreValidate メソッドをオーバーライドしてバリデーターをインターセプトできます。このメソッドは true を返すと検証が続行され、false を返すと検証が終了します。

public class MyValidator : AbstractValidator<Person> {
  public MyValidator() {
    RuleFor(x => x.Name).NotNull();
  }

  protected override bool PreValidate(ValidationContext<Person> context, ValidationResult result) {
    if (context.InstanceToValidate == null) {
      result.Errors.Add(new ValidationFailure("", "Please ensure a model was supplied."));
      return false;
    }
    return true;
  }
}

組み込みバリデーター

FluentValidation にはいくつかの組み込みバリデーターがあり、これらのバリデーターのエラーメッセージは特定のプレースホルダーを使用できます。

NotNull バリデーター

説明:指定されたプロパティが null でないことを確認します。

RuleFor(customer => customer.Surname).NotNull();

使用可能な書式パラメータプレースホルダー: = 検証中のプロパティの名前 = プロパティの現在の値

NotEmpty バリデーター

説明:指定されたプロパティが null、空文字列、またはスペース(または値型のデフォルト値、例:int 0)でないことを確認します。

RuleFor(customer => customer.Surname).NotEmpty();

使用可能な書式パラメータプレースホルダー: = 検証中のプロパティの名前 = プロパティの現在の値

NotEqual バリデーター

説明:指定されたプロパティの値が特定の値(または別のプロパティの値)と等しくないことを確認します。

//Not equal to a particular value
RuleFor(customer => customer.Surname).NotEqual("Foo");

//Not equal to another property
RuleFor(customer => customer.Surname).NotEqual(customer => customer.Forename);

使用可能な書式パラメータプレースホルダー: = 検証中のプロパティの名前 = プロパティが等しくない値

Equal 等価バリデーター

説明:指定されたプロパティの値が特定の値(または別のプロパティの値)と等しいことを確認します。

//Equal to a particular value
RuleFor(customer => customer.Surname).Equal("Foo");

//Equal to another property
RuleFor(customer => customer.Password).Equal(customer => customer.PasswordConfirmation);

使用可能な書式パラメータプレースホルダー: = 検証中のプロパティの名前 = プロパティが等しい値 = プロパティの現在の値

Length 長さバリデーター

特定の文字列プロパティの長さが指定された範囲内にあることを確認します。ただし、文字列プロパティが null であることは確認しません。

RuleFor(customer => customer.Surname).Length(1, 250); //must be between 1 and 250 chars (inclusive)

使用可能な書式パラメータプレースホルダー:

= 検証中のプロパティの名前 = 最小長 = 最大長 = 入力された文字数 = プロパティの現在の値

MaxLength 最大長バリデーター

説明:特定の文字列プロパティの長さが指定された値を超えないことを確認します。

RuleFor(customer => customer.Surname).MaximumLength(250); //must be 250 chars or fewer

使用可能な書式パラメータプレースホルダー: = 検証中のプロパティの名前 = 最大長 = 入力された文字数 = プロパティの現在の値

MinLength 最小長バリデーター

説明:特定の文字列プロパティの長さが指定された値より小さくないことを確認します。

RuleFor(customer => customer.Surname).MinimumLength(10); //must be 10 chars or more

使用可能な書式パラメータプレースホルダー: = 検証中のプロパティの名前 = 最小長 = 入力された文字数 = プロパティの現在の値

LessThan 小なりバリデーター

説明:指定されたプロパティの値が特定の値(または別のプロパティの値)より小さいことを確認します。

//Less than a particular value
RuleFor(customer => customer.CreditLimit).LessThan(100);

//Less than another property
RuleFor(customer => customer.CreditLimit).LessThan(customer => customer.MaxCreditLimit);

使用可能な書式パラメータプレースホルダー: = 検証中のプロパティの名前 = プロパティと比較される値 = プロパティの現在の値

LessThanOrEqualTo 小なりイコールバリデーター

説明:指定されたプロパティの値が特定の値(または別のプロパティの値)以下であることを確認します。

//Less than a particular value
RuleFor(customer => customer.CreditLimit).LessThanOrEqualTo(100);

//Less than another property
RuleFor(customer => customer.CreditLimit).LessThanOrEqualTo(customer => customer.MaxCreditLimit);

使用可能な書式パラメータプレースホルダー: = 検証中のプロパティの名前 = プロパティと比較される値 = プロパティの現在の値

GreaterThan 大なりバリデーター

説明:指定されたプロパティの値が特定の値(または別のプロパティの値)より大きいことを確認します。

//Greater than a particular value
RuleFor(customer => customer.CreditLimit).GreaterThan(0);

//Greater than another property
RuleFor(customer => customer.CreditLimit).GreaterThan(customer => customer.MinimumCreditLimit);

使用可能な書式パラメータプレースホルダー: = 検証中のプロパティの名前 = プロパティと比較される値 = プロパティの現在の値

GreaterThanOrEqualTo 大なりイコールバリデーター

説明:指定されたプロパティの値が特定の値(または別のプロパティの値)以上であることを確認します。

//Greater than a particular value
RuleFor(customer => customer.CreditLimit).GreaterThanOrEqualTo(1);

//Greater than another property
RuleFor(customer => customer.CreditLimit).GreaterThanOrEqualTo(customer => customer.MinimumCreditLimit);

使用可能な書式パラメータプレースホルダー: = 検証中のプロパティの名前 = プロパティと比較される値 = プロパティの現在の値

Must バリデーター

説明:指定されたプロパティの値をデリゲートに渡し、その値に対してカスタム検証ロジックを実行できます。

RuleFor(customer => customer.Surname).Must(surname => surname == "Foo");

使用可能な書式パラメータプレースホルダー: = 検証中のプロパティの名前 = プロパティの現在の値

デリゲートパラメータは、値だけでなく、検証オブジェクト自体も直接渡すことができます。

RuleFor(customer => customer.Surname).Must((customer, surname) => surname != customer.Forename)

正規表現バリデーター

説明:指定されたプロパティの値が指定された正規表現と一致することを確認します。正規表現については、正規表現チュートリアル(サイト管理者注:リンクは無効)を参照してください。

RuleFor(customer => customer.Surname).Matches("some regex here");

使用可能な書式パラメータプレースホルダー: = 検証中のプロパティの名前 = プロパティの現在の値

Email 電子メールバリデーター

説明:指定されたプロパティの値が有効な電子メールアドレス形式であることを確認します。

RuleFor(customer => customer.Email).EmailAddress();

使用可能な書式パラメータプレースホルダー: = 検証中のプロパティの名前 = プロパティの現在の値

カスタムバリデーター

Must バリデーター

カスタムバリデーターを実装する最も簡単な方法は、Must メソッドを使用することです。次のクラスがあるとします。

public class Person {
  public IList<Person> Pets {get;set;} = new List<Person>();
}

リストに少なくとも 10 個の要素が含まれていることを確認するには、次のようにします。

public class PersonValidator:AbstractValidator<Person> {
  public PersonValidator() {
   RuleFor(x => x.Pets).Must(list => list.Count <= 10).WithMessage("The list must contain fewer than 10 items");
  }
}

このロジックを再利用可能にするには、拡張メソッドとしてカプセル化できます。

public static class MyCustomValidators {
  public static IRuleBuilderOptions<T, IList<TElement>> ListMustContainFewerThan<T, TElement>(this IRuleBuilder<T, IList<TElement>> ruleBuilder, int num) {
	return ruleBuilder.Must(list => list.Count < num).WithMessage("The list contains too many items");
  }
}

ここでは、IRuleBuilder の拡張メソッドを作成して再利用可能なロジックを実装しています。使用方法は簡単です。

RuleFor(x => x.Pets).ListMustContainFewerThan(10);

カスタムバリデーターの記述

再利用可能なバリデーターを柔軟に制御したい場合は、Must メソッドを使用してカスタムルールを記述できます。このメソッドを使用すると、検証エラーに関連付けられたインスタンスを手動で作成できます。

public class PersonValidator:AbstractValidator<Person> {
  public PersonValidator() {
   RuleFor(x => x.Pets).Custom((list, context) => {
     if(list.Count > 10) {
       context.AddFailure("The list must contain 10 items or fewer");
     }
   });
  }
}

このメソッドの利点は、同じルールに対して複数のエラーを返すことができることです。

context.AddFailure("SomeOtherProperty", "The list must contain 10 items or fewer");
// Or you can instantiate the ValidationFailure directly:
context.AddFailure(new ValidationFailure("SomeOtherProperty", "The list must contain 10 items or fewer");

カスタムプロパティバリデーター

特定のプロパティに対する検証ロジックが非常に複雑な場合、プロパティベースのカスタムロジックを別のクラスに移動したいことがあります。これは PropertyValidator クラスをオーバーライドすることで行えます。

using System.Collections.Generic;
using FluentValidation.Validators;
public class ListCountValidator<T> : PropertyValidator {
        private int _max;

	public ListCountValidator(int max)
		: base("{PropertyName} must contain fewer than {MaxElements} items.") {
		_max = max;
	}

	protected override bool IsValid(PropertyValidatorContext context) {
		var list = context.PropertyValue as IList<T>;

		if(list != null && list.Count >= _max) {
			context.MessageFormatter.AppendArgument("MaxElements", _max);
			return false;
		}

		return true;
	}
}

PropertyValidator を継承する場合、IsValid メソッドをオーバーライドする必要があります。このメソッドはオブジェクトを受け取り、検証が成功したかどうかを示すブール値を返します。PropertyValidatorContext プロパティを介してアクセスできます。 Instance - 検証中のオブジェクト PropertyDescription - プロパティの名前(または WithName でカスタマイズされた別名) PropertyValue - 検証中のプロパティの値 Member - 検証中のプロパティを記述する MemberInfo

カスタムプロパティバリデーターを使用するには、検証ルールを定義する際に呼び出します。

public class PersonValidator : AbstractValidator<Person> {
    public PersonValidator() {
       RuleFor(person => person.Pets).SetValidator(new ListCountValidator<Pet>(10));
    }
}

ローカライズと多言語

FluentValidation は、デフォルトの検証メッセージに対していくつかの言語の翻訳を提供します。デフォルトでは、現在のスレッドの CurrentUICulture カルチャに基づいて言語が選択されます。WithMessage や WithLocalizedMessage を使用してエラーメッセージを指定することもできます。

WithMessage

Visual Studio の組み込み resx 形式のリソースファイルを使用する場合、WithMessage を呼び出してエラーメッセージをローカライズできます。

RuleFor(x => x.Surname).NotNull().WithMessage(x => MyLocalizedMessages.SurnameRequired);

もちろん、複数の言語をデータベースに保存し、ラムダ式で多言語メッセージを読み取ることもできます。

WithLocalizedMessage

WithLocalizedMessage メソッドを呼び出し、リソースの種類とリソース名を渡すことで、エラーメッセージをローカライズ多言語に対応させることができます。

RuleFor(x => x.Surname).NotNull().WithLocalizedMessage(typeof(MyLocalizedMessages), "SurnameRequired");

デフォルトのエラーメッセージ

FluentValidation のデフォルトのエラーメッセージを置き換える場合は、ILanguageManager インターフェースを実装します。このインターフェースはローカライズ多言語をサポートします。

public class CustomLanguageManager : FluentValidation.Resources.LanguageManager {
  public CustomLanguageManager() {
    AddTranslation("en", "NotNullValidator", "'{PropertyName}' is required.");
  }
}

上記のコードは、NotNullValidator バリデーターの英語のエラーメッセージをカスタマイズしています。もちろん、さらに多くの言語を実装することもできます。LanguageManager を定義したら、起動時に構成する必要があります。

ValidatorOptions.LanguageManager = new CustomLanguageManager();

ローカライズ多言語の無効化

FluentValidation のローカライズサポートを完全に無効にすることができます。これにより、スレッドの CurrentUICulture に関係なく、デフォルトの英語が強制的に使用されます。これは、アプリケーションの起動ルーチンで静的クラス ValidatorOptions を介して行うことができます。

ValidatorOptions.LanguageManager.Enabled = false;

また、デフォルトの言語を強制して、常に特定の言語で表示することもできます。

ValidatorOptions.LanguageManager.Culture = new CultureInfo("fr");

ASP.NET Core との統合

FluentValidation は ASP.NET Core と統合できます。MVC 統合を有効にするには、NuGet パッケージを介してアセンブリ FluentValidation.AspNetCore への参照を追加する必要があります。

Install-Package FluentValidation.AspNetCore

インストール後、名前空間 using FluentValidation.AspNetCore を参照する必要があります。次に、アプリケーションのスタートアップクラス Startup で、services の拡張メソッド AddFluentValidation を呼び出して構成します。

public void ConfigureServices(IServiceCollection services) {
    services.AddMvc(setup => {
      //...mvc setup...
    }).AddFluentValidation();
}

ASP.NET Core がバリデーターを検出できるようにするには、サービスコンテナに登録する必要があります。

public void ConfigureServices(IServiceCollection services) {
    services.AddMvc(setup => {
      //...mvc setup...
    }).AddFluentValidation();

    services.AddTransient<IValidator<Person>, PersonValidator>();
    //etc
}

または、AddFromAssemblyContaining メソッドを使用して、特定のアセンブリ内のすべてのバリデーターを自動的に登録することもできます。

services.AddMvc()
  .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<PersonValidator>());

この例では、Person 型を検証するための PersonValidator バリデーターを定義しています。

public class Person {
	public int Id { get; set; }
	public string Name { get; set; }
	public string Email { get; set; }
	public int Age { get; set; }
}

public class PersonValidator : AbstractValidator<Person> {
	public PersonValidator() {
		RuleFor(x => x.Id).NotNull();
		RuleFor(x => x.Name).Length(0, 10);
		RuleFor(x => x.Email).EmailAddress();
		RuleFor(x => x.Age).InclusiveBetween(18, 60);
	}
}

Person インスタンスを作成して保存するための PeopleController コントローラーを定義し、ModelState.IsValid を使用してユーザー入力が検証に合格したかを判断します。

public class PeopleController : Controller {
	public ActionResult Create() {
		return View();
	}

	[HttpPost]
	public IActionResult Create(Person person) {

		if(! ModelState.IsValid) { // re-render the view when validation failed.
			return View("Create", person);
		}

		Save(person); //Save the person to the database, or some other logic

		TempData["notice"] = "Person successfully created";
		return RedirectToAction("Index");

	}
}

Person を作成するためのビューコードです。

@model Person

<div asp-validation-summary="ModelOnly"></div>

<form asp-action="Create">
  Id: <input asp-for="Id" /> <span asp-validation-for="Id"></span>
  <br />
  Name: <input asp-for="Name" /> <span asp-validation-for="Name"></span>
  <br />
  Email: <input asp-for="Email" /> <span asp-validation-for="Email"></span>
  <br />
  Age: <input asp-for="Age" /> <span asp-validation-for="Age"></span>

  <br /><br />
  <input type="submit" value="submtit" />
</form>

これで、フォームを送信すると、MVC のモデルバインディングシステムが PersonPersonValidator を使用して入力を検証し、検証結果を ModelState に保存して判断します。

ASP.NET Core 組み込み検証との互換性

デフォルトでは、FluentValidation 検証の実行後、MVC ネイティブの DataAnnotations 検証も実行される可能性があります。つまり、FluentValidation と DataAnnotations 検証を混在させて使用できます。この動作を無効にして、FluentValidation を唯一の検証ライブラリにするには、RunDefaultMvcValidationAfterFluentValidationExecutes プロパティを false に設定します。

services.AddMvc().AddFluentValidation(fv => {
 fv.RunDefaultMvcValidationAfterFluentValidationExecutes = false;
});

暗黙的および明示的な子プロパティ検証

複雑なオブジェクトを検証する場合、デフォルトでは、SetValidator メソッドを使用して複雑なプロパティの子バリデーターを手動で指定する必要があります。ASP.NET MVC アプリケーションを実行する場合、子プロパティの暗黙的な検証を有効にすることもできます。この機能を有効にすると、MVC の検証インフラストラクチャが各プロパティのバリデーターを自動的に検索し、子バリデーターを指定する必要がなくなります。これは、SetValidatorImplicitlyValidateChildProperties を true に設定することで行えます。

services.AddMvc().AddFluentValidation(fv => {
 fv.ImplicitlyValidateChildProperties = true;
});

この動作を有効にする場合、手動で SetValidator を介して子プロパティバリデーターを指定しないでください。そうしないと、バリデーターが 2 回実行されます。

クライアント側検証

FluentValidation はサーバー側の検証フレームワークであり、クライアント側の検証を直接提供しません。ただし、jQuery 検証フレームワークでサポートされる HTML 要素のメタデータを生成でき、jquery.validate フレームワークの自動検証をサポートします。

手動検証

MVC プロジェクトでオブジェクトを手動で検証する必要がある場合があります。その場合、検証結果を MVC の ModelState ディクショナリにコピーして、フロントエンドのエラーメッセージ表示に使用できます。

public ActionResult DoSomething() {
  var customer = new Customer();
  var validator = new CustomerValidator();
  var results = validator.Validate(customer);

  results.AddToModelState(ModelState, null);
  return View();
}

AddToModelState メソッドは拡張メソッドとして実装されており、FluentValidation 名前空間の参照が必要です。2 番目のパラメータは省略可能なモデル名プレフィックスで、ModelState ディクショナリ内のオブジェクトプロパティのプレフィックスを設定できます。

バリデーターのカスタマイズ

CustomizeValidatorAttribute を使用して、モデルにバリデーターを指定できます。また、バリデーターにルールセットを指定することもできます。

public ActionResult Save([CustomizeValidator(RuleSet="MyRuleset")] Customer cust) {
  // ...
}

これは、ルールセットをバリデーターに渡すことと同等です。

var validator = new CustomerValidator();
var customer = new Customer();
var result = validator.Validate(customer, ruleSet: "MyRuleset");

この属性は、単一のプロパティの検証を呼び出すためにも使用できます。

public ActionResult Save([CustomizeValidator(Properties="Surname,Forename")] Customer cust) {
  // ...
}

これは、バリデーターに特定のプロパティを指定し、他のプロパティは検証されないことと同等です。

var validator = new CustomerValidator();
var customer = new Customer();
var result = validator.Validate(customer, properties: new[] { "Surname", "Forename" });

また、CustomizeValidatorAttribute 属性を使用して、特定の型の検証をスキップすることもできます。

public ActionResult Save([CustomizeValidator(Skip=true)] Customer cust) {
  // ...
}

バリデーターインターセプター

インターセプターを使用して検証プロセスをさらにカスタマイズできます。インターセプターは、FluentValidation.Mvc 名前空間の IValidatorInterceptor インターフェースを実装する必要があります。

public interface IValidatorInterceptor	{
  ValidationContext BeforeMvcValidation(ControllerContext controllerContext, ValidationContext validationContext);
  ValidationResult AfterMvcValidation(ControllerContext controllerContext, ValidationContext validationContext, ValidationResult result);
}

このインターフェースには 2 つのメソッドがあります。BeforeMvcValidation と AfterMvcValidation で、それぞれ検証前と検証後のプロセスをインターセプトできます。バリデータークラスで直接このインターフェースを実装する以外に、外部でインターフェースを実装し、CustomizeValidatorAttribute 属性を介してインターセプターを指定することもできます。

public ActionResult Save([CustomizeValidator(Interceptor=typeof(MyCustomerInterceptor))] Customer cust) {
 //...
}

この場合、インターセプターは IValidatorInterceptor インターフェースを実装し、パブリックなパラメーターなしのコンストラクターを持つクラスである必要があります。インターセプターは高度なシナリオであり、ほとんどの場合、使用する必要はありませんが、必要に応じて選択できます。

クライアント側のルールセットの指定

デフォルトでは、FluentValidation はルールセットに基づいたクライアント側の検証コードを生成しませんが、RuleSetForClientSideMessagesAttribute を使用してクライアント側のルールセットを指定できます。

[RuleSetForClientSideMessages("MyRuleset")]
public ActionResult Index() {
   return View(new PersonViewModel());
}

コントローラーで SetRulesetForClientsideMessages 拡張メソッド(FluentValidation 名前空間の参照が必要)を使用して、クライアント側のルールセットを指定することもできます。

public ActionResult Index() {
   ControllerContext.SetRulesetForClientsideMessages("MyRuleset");
   return View(new PersonViewModel());
}

ASP.NET MVC 5 との統合

FluentValidation は、古い ASP.NET MVC 5 プロジェクトとも統合できます。NuGet を介してアセンブリ FluentValidation.Mvc5 への参照を追加し、FluentValidation.Mvc 名前空間を参照する必要があります。

Install-Package FluentValidation.Mvc5

インストール後、アプリケーションのグローバルファイル (Global.asax) で Application_Start イベントを構成して、FluentValidation を初期化する必要があります。

protected void Application_Start() {
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    FluentValidationModelValidatorProvider.Configure();
}

MVC 内部では、FluentValidation はバリデーターファクトリを使用して特定の型のバリデーターを作成します。デフォルトでは、属性ベースの AttributedValidatorFactory ファクトリが使用され、指定されたモデルにバリデーターを設定できます。

[Validator(typeof(PersonValidator))]
public class Person {
	public int Id { get; set; }
	public string Name { get; set; }
	public string Email { get; set; }
	public int Age { get; set; }
}

public class PersonValidator : AbstractValidator<Person> {
	public PersonValidator() {
		RuleFor(x => x.Id).NotNull();
		RuleFor(x => x.Name).Length(0, 10);
		RuleFor(x => x.Email).EmailAddress();
		RuleFor(x => x.Age).InclusiveBetween(18, 60);
	}
}

また、MVC に組み込まれている IoC 依存性注入コンテナを使用して、上記の属性マーキング方法の代わりにカスタムバリデーターファクトリを実装することもできます。

FluentValidationModelValidatorProvider.Configure(provider => {
  provider.ValidatorFactory = new MyCustomValidatorFactory();
});

最後に、コントローラーと関連するビューを作成できます。

public class PeopleController : Controller {
	public ActionResult Create() {
		return View();
	}

	[HttpPost]
	public ActionResult Create(Person person) {

		if(! ModelState.IsValid) { // re-render the view when validation failed.
			return View("Create", person);
		}

		TempData["notice"] = "Person successfully created";
		return RedirectToAction("Index");

	}
}

対応するビューコードです。

@Html.ValidationSummary() @using (Html.BeginForm()) { Id: @Html.TextBoxFor(x =>
x.Id) @Html.ValidationMessageFor(x => x.Id)
<br />
Name: @Html.TextBoxFor(x => x.Name) @Html.ValidationMessageFor(x => x.Name)
<br />
Email: @Html.TextBoxFor(x => x.Email) @Html.ValidationMessageFor(x => x.Email)
<br />
Age: @Html.TextBoxFor(x => x.Age) @Html.ValidationMessageFor(x => x.Age)

<br /><br />

<input type="submit" value="submit" />
}

これで、フォームを送信すると、MVC は FluentValidation フレームワークを使用してモデルを検証します。

特別な注意

ASP.NET MVC 5 での FluentValidation フレームワークの使用は、ASP.NET Core と基本的に同じです。そのため、クライアント側検証、手動検証、バリデーターのカスタマイズ、バリデーターインターセプター、クライアント側ルールセットは上記と同様です。記事の長さの都合上、ここでは繰り返しません。

ASP.NET WebApi 2 との統合

FluentValidation の WebApi 統合は、MVC 5 統合(上記)と同じですが、NuGet を介して FluentValidation.WebApi パッケージを参照する必要があります。

出典:https://www.xcode.me/post/5849(サイト管理者注:リンクは無効。ブログパークで転載記事を閲覧可能:https://www.cnblogs.com/mq0036/p/14548370.html)

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2024/06/20

CodeWF.EventBus:軽量イベントバス、コミュニケーションをよりスムーズに

CodeWF.EventBusは、モジュール間の疎結合通信を実現する柔軟なイベントバスライブラリです。WPF、WinForms、ASP.NET Coreなど、さまざまな.NETプロジェクトタイプに対応しています。シンプルな設計で、コマンドのパブリッシュとサブスクライブ、リクエストとレスポンスを簡単に実装できます。順序付けられたイベント処理により、イベントが適切に処理されることを保証します。コードを簡素化し、システムの保守性を向上させます。

続きを読む