Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9318

Blazorにおけるフォームバリデーション手法のまとめ

$
0
0

概要

Blazorにおけるフォームバリデーションの手法に関して紹介します。
下記のようなログインフォームを例にして紹介します。

ファイル名

本記事のデモ(メニューのFormを選択)
ソースコード

前提

.NET Core SDK 3.1.100-preview3-014645
Microsoft.AspNetCore.Blazor 3.1.0-preview2.19528.8
Visual Studio 2019

WebAssembly版(Client版)を使用しています。

また、サンプルではUI要素としてMatBlazorを使用しています。
詳細は下記を参照してください。
https://qiita.com/nobu17/items/ecf2121f7bbb6bc5294b

MatBlazorを使わない場合、一般的なForm要素に置き換えてください。
MatTextField → InputTextもしくはinput
MatButton  → button (type="submit")

基本編

一番多く使う基本的なパターンの実装と説明を行います。

入力モデルの作成

まずは、入力画面にバインドするクラスを定義します。
その上で、各項目に対するバリデーション定義を追加します。
バリデーションを属性で表現するのは、ASP.NET MVC等でもおなじみな方法なので.NET開発者であれば見慣れたものかと思います。

LoginData.cs
publicclassLoginData{[Required(ErrorMessage="ユーザIDを入力してください。")][StringLength(16,ErrorMessage="ユーザIDが長すぎます。")]publicstringUserID{get;set;}[Required(ErrorMessage="パスワードを入力してください。")][StringLength(32,ErrorMessage="パスワードが長すぎます。")]publicstringPassword{get;set;}}

コードビハインドの作成

次にViewにバインドする、先ほどのLoginDataをメンバとして保持するクラスを作成します。
今回はコードビハインドでrazorコンポーネントとC#コードを分離して記載します。
コードビハインドに関しての詳細は、下記を参照してください。
https://qiita.com/nobu17/items/b7dc78db7beb1d833dc8

Form1ViewModel.cs
publicclassForm1ViewModel:ComponentBase{publicLoginDataLoginData{get;set;}=newLoginData();publicvoidSubmit(){// do something}}

ビューの定義

コードビハインドとバインドする画面を作成します。

Form1.razor
@inherits MatTest.Models.Form.Form1ViewModel
<EditFormModel="@LoginData"OnValidSubmit="@Submit"><DataAnnotationsValidator/><ValidationSummary/><MatTextFieldFullWidth="true"Label="UserID"@bind-Value="@LoginData.UserID"></MatTextField><ValidationMessageFor="@(() => LoginData.UserID)"/><MatTextFieldFullWidth="true"Label="Password"@bind-Value="@LoginData.Password"Type="password"></MatTextField><ValidationMessageFor="@(() => LoginData.Password)"/><MatButtonLabel="Login"Outlined="true"Type="submit"></EditForm>

EditForm

フォームで入力する要素をこのタグで囲みます。
- Modelにフォームに入力するプロパティをバインドします。
- OnValidSubmitに入力が正常な場合の確定処理のメソッドをバインドします。
また、不正な入力で確定ボタン押下を検知したい場合には、OnInvalidSubmitイベントをバインドすることで検知できます。

DataAnnotationsValidator

先ほど入力データクラスに付与した属性(Requiredなど)のバリデーションを実施する場合に記載します。

ValidationSummary

バリデーションで発生したエラー内容を表示します。

vali_1.png

ValidationMessage

ValidationSummaryはすべてのエラー内容が表示されるため、
各入力項目に対して個別のバリデーションを表示したい場合に使用します。
For内にラムダでプロパティを指定します。

vali_2.png

カスタムの検証属性

独自のバリデーション属性を作成したい場合は、ValidationAttributeクラスを継承します。
これはBlazor固有というよりも.NETでは一般的に使われている手法です。

CustomeValidationAttribute.cs
publicclassCustomeValidationAttribute:ValidationAttribute{protectedoverrideValidationResultIsValid(objectvalue,ValidationContextvalidationContext){varstr=valueasstring;if(str!=null&&string.IsNullOrWhiteSpace(str)){returnnewValidationResult("空白は無効です。",new[]{validationContext.MemberName});}returnValidationResult.Success;}}

IsValidをオーバーライドして対象のオブジェクトを検証します。

  • 正常の場合はValidationResult.Success戻す
  • 入力エラーの場合は、ValidationResult内にエラーメッセージとvalidationContext.MemberNameを入れて戻す

入力エラー時には下記のようにエラーメッセージが表示されます。

cus.PNG

応用編

基本をベースに、色々な場合を紹介します。

ネストしたクラスに対するバリデーション

DataAnnotationsValidatorはネストしたオブジェクトに対しては、機能しません。
現在はまだプレビュー版となりますが、下記の手順で可能となります。

モジュール追加

Nugetから下記モジュールを追加します。

Microsoft.AspNetCore.Blazor.DataAnnotations.Validation

属性追加

ネストしたクラスのプロパティに対して、ValidateComplexType属性を付与します。

NestedData.cs
publicclassNestedData{[Required][ValidateComplexType]publicLoginDataLoginData{get;set;}=newLoginData();}

バリデータの変更

DataAnnotationsValidatorの代わりにObjectGraphDataAnnotationsValidatorを使用します。

Form2.razor
@inherits MatTest.Models.Form.Form2ViewModel
<EditFormModel="@NestedData"OnValidSubmit="@Submit"><ObjectGraphDataAnnotationsValidator/><ValidationSummary/><MatTextFieldFullWidth="true"Label="UserID"@bind-Value="@NestedData.LoginData.UserID"></MatTextField><MatTextFieldFullWidth="true"Label="Password"@bind-Value="@NestedData.LoginData.Password"Type="password"></MatTextField><MatButtonLabel="Login"Outlined="true"Type="submit"></EditForm>

OSSモジュール(FluentValidation)を使ったカスタムバリデーション

OSSで提供されいてる機能で属性検証以外のバリデーションを作成できます。
FluentValidationといった.NET向けのバリデーションライブラリをBlazor対応させる方法が紹介されているのでそちらを参考に実装します。

https://chrissainty.com/using-fluentvalidation-for-forms-validation-in-razor-components/

パッケージの導入

NugetからFluentValidationをインストールします。

バリデータの作成

FluentValidationの作法に沿ったバリデーションクラスを作成します。

LoginDataValidator.cs
publicclassLoginDataValidator:AbstractValidator<LoginData>{publicLoginDataValidator(){RuleFor(p=>p.UserID).NotEmpty().WithMessage("ログインIDを入力してください。");RuleFor(p=>p.UserID).MaximumLength(10).WithMessage("ログインIDは10文字まで入力してください。");RuleFor(p=>p.Password).NotEmpty().WithMessage("パスワードを入力してください。");RuleFor(p=>p.Password).MaximumLength(10).WithMessage("パスワードは10文字まで入力してください。");}}

AbstractValidatorを継承したクラスを作成します。
(ジェネリクスにはバリデーション対象のクラスを指定)
コンストラクタ内でRuleForラムダで各メンバーのバリデーションを実装します。

コンポーネントの作成

作成したバリデータだけではBlazorではそのまま使えないため、Blazor側のバリデーションに対応させるためのコンポーネントを作成します。
BlazorにはバリデーションのためのEditContextといった仕組みが提供されており、その仕組み内でFluentValidationのバリデーションを行います。
EditContextの詳細に関しては割愛しますが、下記等が参考になります。
https://gunnarpeipman.com/blazor-form-validation/

掲載元(掲載時より、一部APIの仕様が変わっているのでその対応を行っています。AddRangeをAddに変更。参考)

FluentValidationValidator.cs
publicclassFluentValidationValidator:ComponentBase{[CascadingParameter]EditContextCurrentEditContext{get;set;}protectedoverridevoidOnInitialized(){if(CurrentEditContext==null){thrownewInvalidOperationException($"{nameof(FluentValidationValidator)} requires a cascading "+$"parameter of type {nameof(EditContext)}. For example, you can use {nameof(FluentValidationValidator)} "+$"inside an {nameof(EditForm)}.");}CurrentEditContext.AddFluentValidation();}}publicstaticclassEditContextFluentValidationExtensions{publicstaticEditContextAddFluentValidation(thisEditContexteditContext){if(editContext==null){thrownewArgumentNullException(nameof(editContext));}varmessages=newValidationMessageStore(editContext);editContext.OnValidationRequested+=(sender,eventArgs)=>ValidateModel((EditContext)sender,messages);editContext.OnFieldChanged+=(sender,eventArgs)=>ValidateField(editContext,messages,eventArgs.FieldIdentifier);returneditContext;}privatestaticvoidValidateModel(EditContexteditContext,ValidationMessageStoremessages){varvalidator=GetValidatorForModel(editContext.Model);varvalidationResults=validator.Validate(editContext.Model);messages.Clear();foreach(varvalidationResultinvalidationResults.Errors){messages.Add(editContext.Field(validationResult.PropertyName),validationResult.ErrorMessage);}editContext.NotifyValidationStateChanged();}privatestaticvoidValidateField(EditContexteditContext,ValidationMessageStoremessages,inFieldIdentifierfieldIdentifier){varproperties=new[]{fieldIdentifier.FieldName};varcontext=newValidationContext(fieldIdentifier.Model,newPropertyChain(),newMemberNameValidatorSelector(properties));varvalidator=GetValidatorForModel(fieldIdentifier.Model);varvalidationResults=validator.Validate(context);messages.Clear(fieldIdentifier);// APIの仕様変更のため、Addに変更//messages.AddRange(fieldIdentifier, validationResults.Errors.Select(error => error.ErrorMessage));messages.Add(fieldIdentifier,validationResults.Errors.Select(error=>error.ErrorMessage));editContext.NotifyValidationStateChanged();}privatestaticIValidatorGetValidatorForModel(objectmodel){varabstractValidatorType=typeof(AbstractValidator<>).MakeGenericType(model.GetType());varmodelValidatorType=Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t=>t.IsSubclassOf(abstractValidatorType));varmodelValidatorInstance=(IValidator)Activator.CreateInstance(modelValidatorType);returnmodelValidatorInstance;}}

実装

作成したバリデータコンポーネント(FluentValidationValidator)を配置します。

Form3.razor
<EditFormModel="@LoginData"OnValidSubmit="@HandleValidSubmit"class="mat-layout-grid-cell mat-layout-grid-cell-span-12"><FluentValidationValidator/><ValidationSummary/><MatTextFieldFullWidth="true"Label="UserID"@bind-Value="@LoginData.UserID"></MatTextField><MatTextFieldFullWidth="true"Label="Password"@bind-Value="@LoginData.Password"Type="password"></MatTextField><MatButtonLabel="Login"Outlined="true"Type="submit"></MatButton></EditForm>

まとめ

Blazorにおけるフォームバリデーション手法に関してまとめました。
バリデーション手法は従来の.NETの手法を踏襲しているため、親しみがある人も多いのではないでしょうか。

Blazorのその他の投稿記事

何点かBlazorに関して記事を書いていますので、良ければ見てみてください。

参考資料

https://docs.microsoft.com/ja-jp/aspnet/core/blazor/forms-validation?view=aspnetcore-3.0
https://gunnarpeipman.com/blazor-form-validation/
https://chrissainty.com/using-fluentvalidation-for-forms-validation-in-razor-components/
https://blazor-university.com/forms/writing-custom-validation/
http://blazorhelpwebsite.com/Blog/tabid/61/EntryId/4337/Blazor-Forms-and-Validation.aspx
https://remibou.github.io/Client-side-validation-with-Blazor-and-Data-Annotations/
https://dzone.com/articles/blazor-form-validation
https://itnext.io/blazor-forms-and-validation-418173350435


Viewing all articles
Browse latest Browse all 9318

Latest Images

Trending Articles