検証
ヒント
この内容は電子ブック『.NET MAUI を使用したエンタープライズ アプリケーション パターン』からの抜粋です。これは .NET Docs で閲覧することも、無料の PDF をダウンロードしてオフラインで読むこともできます。
ユーザーから入力を受け取るアプリでは、その入力が有効であることを確認する必要があります。 たとえば、アプリでは、入力に特定の範囲の文字のみが含まれているかどうか、入力が一定の長さであるかどうか、または特定の形式に一致しているかどうかを確認します。 検証を行わないと、ユーザーはアプリが失敗する原因となるデータを提供する危険性があります。 適切な検証によって、ビジネス ルールが適用され、攻撃者が悪意のあるデータを挿入するのを防ぐのに役立ちます。
Model-View-ViewModel (MVVM) パターンのコンテキストでは、多くの場合、データの検証を実行して、ユーザーが修正できるように検証エラーをビューに通知するために、ビュー モデルまたはモデルが必要です。 eShop マルチプラットフォーム アプリでは、ビュー モデルのプロパティの同期クライアント側検証を実行し、無効なデータを含むコントロールを強調表示して、データが無効である理由をユーザーに通知するエラー メッセージを表示することで、検証エラーをユーザーに通知します。 次の図は、eShop マルチプラットフォーム アプリでの検証の実行に関連するクラスを示します。
検証を必要とするビュー モデル プロパティは型 ValidatableObject<T>
であり、各 ValidatableObject<T>
インスタンスには、その Validations
プロパティに検証規則が追加されています。 検証は、ValidatableObject<T>
インスタンスの Validate
メソッドを呼び出すことによってビュー モデルから呼び出されます。これにより、検証規則が取得され、ValidatableObject<T>.Value
プロパティに対して実行されます。 検証エラーはすべて ValidatableObject<T>
インスタンスの Errors
プロパティに配置され、検証が成功したか失敗したかを示すために ValidatableObject<T>
インスタンスの IsValid
プロパティが更新されます。 次のコードは、ValidatableObject<T>
の実装を示します。
using CommunityToolkit.Mvvm.ComponentModel;
namespace eShop.Validations;
public class ValidatableObject<T> : ObservableObject, IValidity
{
private IEnumerable<string> _errors;
private bool _isValid;
private T _value;
public List<IValidationRule<T>> Validations { get; } = new();
public IEnumerable<string> Errors
{
get => _errors;
private set => SetProperty(ref _errors, value);
}
public bool IsValid
{
get => _isValid;
private set => SetProperty(ref _isValid, value);
}
public T Value
{
get => _value;
set => SetProperty(ref _value, value);
}
public ValidatableObject()
{
_isValid = true;
_errors = Enumerable.Empty<string>();
}
public bool Validate()
{
Errors = Validations
?.Where(v => !v.Check(Value))
?.Select(v => v.ValidationMessage)
?.ToArray()
?? Enumerable.Empty<string>();
IsValid = !Errors.Any();
return IsValid;
}
}
プロパティ変更通知は ObservableObject
クラスによって提供されるため、Entry
コントロールをビュー モデル クラスの ValidatableObject<T>
インスタンスの IsValid
プロパティにバインドして、入力されたデータが有効であるかどうかを通知できます。
検証規則の指定
検証規則を指定するには、次のコード例に示す IValidationRule<T>
インターフェイスから派生するクラスを作成します。
public interface IValidationRule<T>
{
string ValidationMessage { get; set; }
bool Check(T value);
}
このインターフェイスは、検証規則クラスで、必要な検証の実行に使用されるブール型 Check
メソッドと、値が、検証が失敗した場合に表示される検証エラー メッセージである ValidationMessage
プロパティを提供する必要があることを指定します。
次のコード例は、eShop マルチプラットフォーム アプリでモック サービスを使用するときに LoginView
でユーザーが入力したユーザー名とパスワードの検証を実行するために使用される IsNotNullOrEmptyRule<T>
検証規則を示します。
public class IsNotNullOrEmptyRule<T> : IValidationRule<T>
{
public string ValidationMessage { get; set; }
public bool Check(T value) =>
value is string str && !string.IsNullOrWhiteSpace(str);
}
Check
メソッドは、値引数が null または空であるかどうか、あるいは空白文字のみで構成されているかどうかを示すブール値を返します。
次のコード例は、eShop マルチプラットフォーム アプリでは使用されませんが、メール アドレスを検証するための検証規則を示します。
public class EmailRule<T> : IValidationRule<T>
{
private readonly Regex _regex = new(@"^([w.-]+)@([w-]+)((.(w){2,3})+)$");
public string ValidationMessage { get; set; }
public bool Check(T value) =>
value is string str && _regex.IsMatch(str);
}
Check
メソッドは、値引数が有効なメール アドレスであるかどうかを示すブール値を返します。 これは、Regex
コンストラクターで指定された正規表現パターンが最初に出現する値引数を検索することによって実現されます。 入力文字列で正規表現パターンが見つかったかどうかは、value
と Regex.IsMatch を照合することで判断できます。
Note
プロパティの検証には、依存プロパティが含まれる場合があります。 依存プロパティの一例としては、プロパティ A の有効な値のセットが、プロパティ B で設定された特定の値に依存する場合があります。プロパティ A の値が許可された値の 1 つであることを確認するには、プロパティ B の値を取得する必要があります。さらに、プロパティ B の値が変更された場合、プロパティ A を再検証する必要があります。
検証規則をプロパティに追加する
eShop マルチプラットフォーム アプリでは、検証を必要とするビュー モデル プロパティは型 ValidatableObject<T>
として宣言されます。この T
は、検証されるデータの型です。 次のコード例は、このような 2 つのプロパティの例を示します。
public ValidatableObject<string> UserName { get; private set; }
public ValidatableObject<string> Password { get; private set; }
検証を行うには、次のコード例に示すように、検証規則を各 ValidatableObject<T>
インスタンスの検証コレクションに追加する必要があります。
private void AddValidations()
{
UserName.Validations.Add(new IsNotNullOrEmptyRule<string>
{
ValidationMessage = "A username is required."
});
Password.Validations.Add(new IsNotNullOrEmptyRule<string>
{
ValidationMessage = "A password is required."
});
}
このメソッドは、IsNotNullOrEmptyRule<T>
検証規則を、各 ValidatableObject<T>
インスタンスの Validations
コレクションに追加して、検証規則の ValidationMessage
プロパティの値を指定します。これは、検証が失敗した場合に表示される検証エラー メッセージを指定します。
検証のトリガー
eShop マルチプラットフォーム アプリで使用される検証アプローチでは、プロパティの検証を手動でトリガーし、プロパティが変更されたときに自動的に検証をトリガーできます。
手動による検証のトリガー
ビュー モデル プロパティについては、検証を手動でトリガーできます。 たとえば、モック サービスを使用している場合、ユーザーが LoginView
で Login
ボタンをタップすると、eShop マルチプラットフォーム アプリでこれが発生します。 コマンド デリゲートは LoginViewModel
で MockSignInAsync
メソッドを呼び出します。これは、次のコード例に示すように、Validate
メソッドを実行して検証を呼び出します。
private bool Validate()
{
bool isValidUser = ValidateUserName();
bool isValidPassword = ValidatePassword();
return isValidUser && isValidPassword;
}
private bool ValidateUserName()
{
return _userName.Validate();
}
private bool ValidatePassword()
{
return _password.Validate();
}
Validate
メソッドは、各 ValidatableObject<T>
インスタンスで Validate
メソッドを呼び出して、LoginView
でユーザーが入力したユーザー名とパスワードの検証を実行します。 次のコード例は、ValidatableObject<T>
クラスの Validate
メソッドを示します。
public bool Validate()
{
Errors = _validations
?.Where(v => !v.Check(Value))
?.Select(v => v.ValidationMessage)
?.ToArray()
?? Enumerable.Empty<string>();
IsValid = !Errors.Any();
return IsValid;
}
このメソッドは、オブジェクトの Validations
コレクションに追加されたすべての検証規則を取得します。 取得された各検証規則の Check
メソッドが実行され、データの検証に失敗した検証規則の ValidationMessage
プロパティ値が ValidatableObject<T>
インスタンスの Errors
コレクションに追加されます。 最後に、IsValid
プロパティが設定され、その値が呼び出し元のメソッドに返され、検証が成功したか失敗したかを示します。
プロパティが変更された場合の検証のトリガー
検証はまた、バウンドされたプロパティが変更されるたびに自動的に実行されます。 たとえば、LoginView
内の両方向のバインドによって UserName
または Password
プロパティが設定されると、検証がトリガーされます。 次のコード例は、これがどのように発生するかを示します。
<Entry Text="{Binding UserName.Value, Mode=TwoWay}">
<Entry.Behaviors>
<behaviors:EventToCommandBehavior
EventName="TextChanged"
Command="{Binding ValidateUserNameCommand}" />
</Entry.Behaviors>
</Entry>
Entry
コントロールが ValidatableObject<T>
インスタンスの UserName.Value
プロパティにバインドされ、このコントロールの Behaviors
コレクションには、EventToCommandBehavior
インスタンスが追加されています。 この動作によって、Entry
で発生する TextChanged
イベントに応答して ValidateUserNameCommand
が実行されます。これは、Entry
内のテキストが変更されたときに発生します。 次に ValidateUserNameCommand
デリゲートによって ValidateUserName
メソッドが実行され、これは ValidatableObject<T>
インスタンスで Validate
メソッドを実行します。 したがって、ユーザーがユーザー名の Entry
コントロールに文字を入力するたびに、入力されたデータの検証が実行されます。
検証エラーの表示
eShop マルチプラットフォーム アプリでは、無効なデータを含むコントロールを赤い背景で強調表示し、無効なデータを含むコントロールの下にデータが無効である理由をユーザーに通知するエラー メッセージを表示することで、検証エラーをユーザーに通知します。 無効なデータが修正されると、背景は既定の状態に戻り、エラー メッセージは削除されます。 次の図は、検証エラーが発生した場合の eShop マルチプラットフォーム アプリ内の LoginView
を示します。
無効なデータを含むコントロールの強調表示
.NET MAUI には、検証情報をユーザーに提供するためのさまざまな方法が用意されていますが、最も簡単な方法の 1 つは、Triggers
を使用することです。 Triggers
を使用すると、コントロールに対して発生するデータの変更に基づいてコントロールの状態 (通常は外観) を変更することができます。 検証では、DataTrigger
を使用します。これは、バウンドされたプロパティから発生した変更をリッスンし、その変更に応答します。 LoginView
の Entry
コントロールは、次のコードを使用してセットアップされます。
<Entry Text="{Binding UserName.Value, Mode=TwoWay}">
<Entry.Style>
<OnPlatform x:TypeArguments="Style">
<On Platform="iOS, Android" Value="{StaticResource EntryStyle}" />
<On Platform="WinUI" Value="{StaticResource WinUIEntryStyle}" />
</OnPlatform>
</Entry.Style>
<Entry.Behaviors>
<mct:EventToCommandBehavior
EventName="TextChanged"
Command="{Binding ValidateCommand}" />
</Entry.Behaviors>
<Entry.Triggers>
<DataTrigger
TargetType="Entry"
Binding="{Binding UserName.IsValid}"
Value="False">
<Setter Property="BackgroundColor" Value="{StaticResource ErrorColor}" />
</DataTrigger>
</Entry.Triggers>
</Entry>
DataTrigger
では、次のプロパティを指定します。
プロパティ | 説明 |
---|---|
TargetType |
トリガーが属するコントロールの種類。 |
Binding |
トリガー条件の変更通知と値を提供するデータ Binding マークアップ。 |
Value |
トリガーの条件が満たされたときに指定するデータ値。 |
この Entry
では、LoginViewModel.UserName.IsValid
プロパティの変更をリッスンします。 このプロパティで変更が発生するたびに、値が、DataTrigger
で設定された Value
プロパティと比較されます。 値が等しい場合、トリガー条件が満たされ、DataTrigger
に提供された Setter
オブジェクトが実行されます。 このコントロールには、BackgroundColor
プロパティを、StaticResource
マークアップを使用して定義されたカスタム カラーに更新する単一の Setter
オブジェクトがあります。 Trigger
条件が満たされなくなった場合、コントロールは Setter
オブジェクトによって設定されたプロパティを以前の状態に戻します。 Triggers
の詳細については、「.NET MAUI Docs: トリガー」を参照してください。
エラー メッセージの表示
UI では、データが検証に失敗した各コントロールの下の Label コントロールに検証エラー メッセージが表示されます。 次のコード例は、ユーザーが有王なユーザー名を入力しなかった場合に検証エラー メッセージを表示する Label
を示します。
<Label
Text="{Binding UserName.Errors, Converter={StaticResource FirstValidationErrorConverter}"
Style="{StaticResource ValidationErrorLabelStyle}" />
各 Label は、検証対象のビュー モデル オブジェクトの Errors
プロパティにバインドされます。 Errors
プロパティは ValidatableObject<T>
クラスによって提供され、型 IEnumerable<string>
です。 Errors
プロパティには複数の検証エラーが含まれる可能性があるため、最初のエラーを取得して表示するには、FirstValidationErrorConverter
インスタンスを使用します。
まとめ
eShop マルチプラットフォーム アプリでは、ビュー モデルのプロパティの同期クライアント側検証を実行し、無効なデータを含むコントロールを強調表示して、データが無効である理由をユーザーに通知するエラー メッセージを表示することで、検証エラーをユーザーに通知します。
検証を必要とするビュー モデル プロパティは型 ValidatableObject<T>
であり、各 ValidatableObject<T>
インスタンスには、その Validations
プロパティに検証規則が追加されています。 検証は、ValidatableObject<T>
インスタンスの Validate
メソッドを呼び出すことによってビュー モデルから呼び出されます。これにより、検証規則が取得され、ValidatableObject<T>
Value プロパティに対して実行されます。 検証エラーはすべて ValidatableObject<T>
インスタンスの Errors
プロパティに配置され、検証が成功したか失敗したかを示すために ValidatableObject<T>
インスタンスの IsValid プロパティが更新されます。
.NET