ASP.NET MVC 3 - Custom Remote Validation と Custom Validator の併用

ASP.NET MVC 3 の Remote Validation を使うと、フォームをPostせずに入力値の検証を行うことができます。
前回書いたのは、デフォルトの検証属性をクライアント側で実行する方法についてでした。

今回は独自の検証ロジックを実装し、Postせずに検証を行うCustom Remote Validationについてです。
ただし通常の実装と異なり、Custom Validator も併用します。

通常のCustom Remote Validation については
How to: Implement Remote Validation in ASP.NET MVCの[Adding Custom Remote Validation]を
読んでください。

(注意)以下は通常の実装とは異なるので、興味ある方のみ参考程度にしてください。

Custom Remote Validationの実装に、Custom Validator を併用しようと考えた理由は、以下のとおりです。

1.
Custom Remote Validation を用いた通常の実装では、Controllerに検証ロジックを書くスタイルとなる。
これはASP.NET MVC2 以降の、データの検証ロジックはModelに実装するスタイルに反すると思う。

2.
Custom Validator をクライアント側で実行する場合、クライアントサイドスクリプトを書く必要がある。
このスクリプトは、サーバー側の検証ロジックと重複することになる(重複ロジックは嫌です。)。

3.
Custom Remote Validation のみでは、クライアント(Postする前)で検証が呼び出されない場合に
サーバー側(Postした後)でチェックが実行されない。

以上を踏まえ、
・クライアント側、サーバー側の両方で検証ロジックを実行し、
・検証ロジックはModel内に統一する
ことを念頭に以下のロジックを実装しました。


1.画面と仕様
f:id:UnderSourceCode:20130504103521j:plain
自動生成されるテンプレートの[Register]画面に検証を追加します。
検証の内容は、Password欄の入力値が特定の文字列であった場合はエラーとするもので、
クライアント側(Postする前)、サーバー側(Postした後)に行います。

2.Model


public class RegisterModel
{
(略)
[Required]
[ValidatePasswordLength]
[Remote("ValidatePasswordString", "Validation")]
[ValidatePasswordString]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }

(略)


画面にて入力する項目を格納するModelです。
Passwordプロパティに
・Custom Remote Validation を呼び出す [Remote("ValidatePasswordString", "Validation")]
・Custom Validator を呼び出す [ValidatePasswordString]
を新たに属性として追加しています。

3.Custom Validator


public sealed class ValidatePasswordStringAttribute : ValidationAttribute
{
private const string _defaultErrorMessage = "'{0}' isn't available.";

public override bool IsValid(object value)
{
var valueAsString = (value as string).Trim();
this.ErrorMessage = FormatErrorMessage(valueAsString);

var lowerValueAsString = valueAsString.ToLower();
List list = new List(){"password", "123456", "aaaaaa"};
return !list.Contains(lowerValueAsString);
}

// フレームワークによって呼び出されるのを防ぐため、privateで定義する。
private string FormatErrorMessage(string value)
{
return string.Format(_defaultErrorMessage, value);
}
}


検証ロジックを、Model内にCustom Validator として実装します。
IsValid()の中で、画面の入力値が特定の値かどうかを判定しています。
これくらいなら、RegularExceptionを使えますが・・・(汗)
まあ、サーバー側で独自のエラーチェックを行う例だと思ってください。(笑)

4.Custom Remote Validation


public class ValidationController : Controller
{
public JsonResult ValidatePasswordString(string password)
{
var attribute = new Models.ValidatePasswordStringAttribute();
if(attribute.IsValid(password))
{
return Json(true, JsonRequestBehavior.AllowGet);
}
else
{
return Json(attribute.ErrorMessage, JsonRequestBehavior.AllowGet);
}
}
}

今回の肝である、Custom Remote Validation です。Controller として実装しています。
クライアントから呼び出されるValidatePasswordString()では、attribute.IsValid(password)で
Custom Validator のエラーチェックロジックを呼び出しています。

5.Web.config


<appSettings>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>

Client-Side Validation を有効にするためには、上の2つの値をtrueにする必要があります。
前回と同じです。

3.Register.cshtml


<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>

必要なJavaScriptファイルをインクルードしています。
前回と同じです。

以上で、クライアント側、サーバー側の検証ロジックを
Model内のCustom Validator に統一することができました。