MVC 3におけるカスタム属性による検証について
MVC 2でもカスタム属性検証はサーバーサイドとクライアントサイドともに実装できました(詳細はMVC 2記事を参照してください)。DataAnnotationが提供する検証属性クラスはValidationAttributeクラスをベースとして作られているので、ValidationAttributeクラスを継承し、必要な記載を追加することで、簡単にカスタム検証属性が作成できます
MVC 3では、IClientValidatableインターフェースを実装することで、MVC 2の時では手間だとも言えたアダプタクラスの作成が簡略化され、クラスを1つにまとめれられるようになりました。また、クライアントサイドの検証の実装として昨今のWebのトレンドでもある"控えめなJavaScript"を利用しています(図6)。
WebアプリケーションによりJavaScriptが必須となり、高機能で軽快なWebアプリケーションが多く作られてきました。その過程としてHTMLの内部にJavaScriptが多く記載され、可読性の低下や保守性を損なうということを多くの開発者が感じてきていました。この悩ましい部分を改善するために出てきたアプローチが"控えめなJavaScript"です。
HTMLの内部に最低限のパラメータ情報のみを残し、JavaScriptを外部ライブラリに分離させ、HTMLとJavaScriptを明確に分離することを指します。
ASP.NET MVCでは、既定のクライアントサイド検証でこの控えめなJavaScriptが利用されています。
<input class="text-box single-line" data-val="true" data-val-regex="電子メールアドレスを入力してください。" data-val-regex-pattern="[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}" data-val-required="Eメールアドレス フィールドが必要です。" id="Email" name="Email" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="Email" data-valmsg-replace="true"></span>
上記にパラメータとしてdata-XXXという項目が多くあることが確認できますが、これが控えめなJavaScriptです。
IClientValidatableインターフェースはIClientValidatable.GetClientValidationRulesメソッドだけを定義しています。このGetClientValidationRulesメソッド内でクライアントサイドに検証ルールを渡す設定を記載します。
大まかな手順は以下のとおりです。
- サーバーサイドにカスタム属性を作成
- IClientValidatableインターフェースの実装と、検証ルールの記載
- jQuery Validationにクライアントサイドの検証を追加
サーバーサイドにカスタム属性を作成
今回は汎用的とは言えませんが、文字が入力されている場合に文字数が30文字以上かどうかを判定するカスタム属性を作成した上で、クライアントサイドの検証までを実装してみます。実行結果自体は図7のようになります。
では、最初にサーバーサイドのカスタム属性の作成です。
using System.ComponentModel.DataAnnotations;
public class CheckInputStringAttribute : ValidationAttribute
{
public CheckInputStringAttribute()
{
// 既定のエラーメッセージを設定
this.ErrorMessage = "文字を入力する場合は30文字以上入力してください。";
}
public override bool IsValid(object value)
{
// 入力値をstring型にキャスト
string _value = (string)value;
// null文字、空白スペース以外で、30文字以内かどうかの判定
if ((string.IsNullOrEmpty(_value) == false) && _value.Length < 30)
{
return false; // 検証失敗
}
else
{
return true; // 検証成功
}
}
}
データの検証を行うIsValidメソッドをオーバーライドし、内部に検証ロジックを記載します。文字が入力されていて30文字以内かどうかの判定を実施します。これで属性は完成です。この時点で属性クラスとしての活用ができます。
IClientValidatableインターフェースの実装と、検証ルールの記載
続いてIClientValidatableインターフェースの実装です。上記コードから追記している部分を太字で記載します。
using System.Collections.Generic;
using System.Web.Mvc;
using System.ComponentModel.DataAnnotations;
public class CheckInputStringAttribute : ValidationAttribute, IClientValidatable
{
public CheckInputStringAttribute()
{
// 既定のエラーメッセージを設定
this.ErrorMessage = "文字を入力する場合は30文字以上入力してください。";
}
public override bool IsValid(object value)
{
// 入力値をstring型にキャスト
string _value = (string)value;
// null文字、空白スペース以外で、30文字以内かどうかの判定
if ((string.IsNullOrEmpty(_value) == false) && _value.Length < 30)
{
return false; // 検証失敗
}
else
{
return true; // 検証成功
}
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
// クライアントサイド検証のルールを設定
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "checkinputstring",
};
// パラメータがある場合はここで値を設定
// クライアントサイド検証のルールを返す
yield return rule;
}
}
IClientValidatableインターフェースを実装すると、IClientValidatable.GetClientValidationRulesメソッドを実装することになりますが、戻り値がIEnumerable<ModelClientValidationRule>を使用するためSystem.Collections.Generic名前空間とSystem.Web.Mvc名前空間をそれぞれ宣言します。
GetClientValidationRulesメソッドのパラメータは以下のとおりです。
- モデルのメタデータ(metadata)
- モデルのコントローラーのコンテキスト(context)
ModelClientValidationRuleオブジェクトはJSON形式で検証ルールを渡すコンテナです。オーバーライドしたメソッド内ではJSON形式の変数を用意して返しているだけです。JSONの値となるrule変数内に、ModelClientValidationRuleオブジェクトを格納しています。rule変数内にはErrorMessageプロパティにエラーメッセージを、ValidationTypeプロパティに検証の型を指定しています。エラーメッセージ自身はModelMetadata.GetDisplayNameメソッドを使用してモデルの表示名を取得し、エラーメッセージの書式を適用するFormatErrorMessageメソッドを使用して格納します。
最後にyield returnにステートメントを使い、ルールを返します。
jQuery Validationにクライアントサイドの検証を追加
これまでの設定では、JSON形式のデータだけが渡されている状態なので、クライアントサイドの検証はできません。クライアントサイドでもサーバーサイドの属性同様の検証ロジックを記載することになります。新しくJavaScriptファイルを追加し(サンプルではcheckinputstring.js)、以下のコードを記載します。
// jQuery Validationにクライアントサイド検証を登録
jQuery.validator.addMethod("checkinputstring", function (value, element, param) {
// 入力チェック
if (!value) {
return true;
// 30文字以内かどうかの判定
}else if(value.length < 30){
return false;
} else {
return true;
}
});
// クライアントサイド検証を控えめなJavaScriptで呼び出すように登録
jQuery.validator.unobtrusive.adapters.addBool('checkinputstring');
jQuery.validator.addMethodメソッドでクライアントサイドの検証を登録します。第一パラメータに検証メソッド名を、第二パラメータに実行するコールバック関数を指定します。コールバック関数自身はサーバーサイド検証と同様の検証処理を記載します。
最後に、jQuery.validator.unobtrusive.adapters.addBoolメソッドを使用し、控えめなJavaScriptによるクライアントサイド検証を実施するために登録しています。addboolメソッドはパラメータに検証名を指定します。もしクライアントサイド検証にパラメータがある場合はaddSingleValメソッドを使用するといいでしょう。第一パラメータに検証名を、第二パラメータにパラメータ名を指定します。
最後に、作成したJavaScriptファイルを利用する箇所であるCreate.cshtml、Edit.cshtmlファイルに追加します。既にjQuery Validationのファイルが記載されているので、その下に追記します。
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/checkinputstring.js")" type="text/javascript"></script>
以上の設定で、サーバーサイド検証、クライアントサイド検証ともに実装完了です。実行後、サーバーに問い合わせる前にクライアントサイド検証が動作することを確認できます。
まとめ
今回はMVC 3を利用する際の検証周りの機能についてご紹介しました。MVC 2である程度骨組みが決まり、MVC 3では検証を実施しやすくする機能強化がされていることを感じ取れたのではないでしょうか。
次回はControllerや単体テスト周りの活用方法を紹介予定です。お楽しみに。


