2012/10/02

【ASP.Net】個人的な検証機能のおさらい クライアントサイド編【MVC】

 サーバーサイド編に続き、今回はクライアント編です。

 ひとえにクライアント編とは言ってもクライアント側であればスクリプト次第で色々と方法はあるとは思いますが、今回はjQueryのValidateプラグインを使ってみます。
 まずは基本的な使い方から。

Editビューへの追加(一部抜粋)
<script src="~/Scripts/jquery-1.8.2.min.js" type="text/javascript"></script>
<script src="~/Scripts/jquery.validate.min.js" type="text/javascript"></script>
<script type="text/javascript">
    $(function () {
        $('form').validate({
            rules: {
                UserName: { maxlength: 50 },
                Birthday: { date: true },
                Email: {
                    required: true,
                    email: true
                },
                CompareEmail: {
                    required: true,
                    equalTo: '#Email'
                },
                Tell: { number: true }
            },
            messages: {
                UserName: 'UserNameは最大50字までです' ,
                Birthday: 'Birthdayが不正です',
                Email: {
                    required: 'Emailは必須項目です',
                    email: 'Emailが不正です'
                },
                CompareEmail: {
                    required: 'Emailは必須項目です',
                    equalTo: '入力されたEmailと異なっています'
                },
                Tell: 'Tellが不正です'
            }
        })
    });
</script>

 マスターページやレイアウトページがあるならjQuery本体やValidateプラグインはそっちに配置してしまえばOKです。検証の設定は見ての通りって感じですが、一応解説すると「$(’form’).validate(…)」でこのページのすべてのformを検証の対象としています。「rules:」プロパティは各name属性に検証のルールを定義しています。「messages:…」は引っかかった際のメッセージを定義しています。プロパティ等の詳細についてはこちら(Plugins/Validation - jQuery Wiki)を確認してください。

実行結果

image

 やっぱりプラグインは便利ですね~。でも、これじゃサーバーサイドの定義とクライアントサイドの定義で2度手間じゃねーの?って感じちゃいます。そこでMS製のValidateプラグインの派生プラグイン(?)「jquery.validate.unobtrusive」を利用します。これはNuGetで取得できます。これを読み込むだけでサーバーサイドで定義した検証をクライアントサイドで動作させることができます。

Editビューへの追加(一部抜粋)
<script src="~/Scripts/jquery-1.8.2.min.js" type="text/javascript"></script>
<script src="~/Scripts/jquery.validate.min.js" type="text/javascript"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js" type="text/javascript"></script>
<!-- とりあえず以下はコメントアウトしときます
<script type="text/javascript">
    $(function () {
        $('form').validate({
            rules: {
                UserName: { maxlength: 50 },
                Birthday: { date: true },
                Email: {
                    required: true,
                    email: true
                },
                CompareEmail: {
                    required: true,
                    equalTo: '#Email'
                },
                Tell: { number: true }
            },
            messages: {
                UserName: 'UserNameは最大50字までです',
                Birthday: 'Birthdayが不正です',
                Email: {
                    required: 'Emailは必須項目です',
                    email: 'Emailが不正です'
                },
                CompareEmail: {
                    required: 'Emailは必須項目です',
                    equalTo: '入力されたEmailと異なっています'
                },
                Tell: 'Tellが不正です'
            }
        })
    });
</script>
-->


 ほんとに読み込んだだけですが、実行してみましょう。

実行結果(Post発生前)

image

 見ての通り、Post発生前でありながら前回のPost発生後と同じ画面になっています。たったこれだけで、前半部分が無駄になっちゃいました。まぁValidateプラグインの知識のひとつということにしときます。

 さて、次は独自の検証機能を持った属性を用意してサーバーサイドとクライアントサイドで連携してみたいと思います。前回紹介しなかった「System.ComponentModel.DataAnnotations.ValidationAttribute」クラスの出番です。これと「System.Web.Mvc.IClientValidatable」インターフェースを組み合わせて利用します。早速、独自検証クラスを作ります。検証内容は「Birthdayが18歳以上か?」で考えてみます。

独自検証クラス
public class UserMinorAttribute : ValidationAttribute, IClientValidatable
{
    //エラーメッセージの初期化
    public UserMinorAttribute()
    {
        this.ErrorMessage = "18未満の方は登録できません。(サーバーサイド)";
    }
 
    //検証
    public override bool IsValid(object value)
    {
        var birthday = value as DateTime?;
 
        if (birthday != null && birthday <= DateTime.Now.AddYears(-18))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
 
    //クライアントサイド用定義
    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule
            {
                ErrorMessage = "18未満の方は登録できません。(クライアントサイド)",
                ValidationType = "userminor"
            };
 
        yield return rule;
    }
 
}

 インターフェースのメンバである「GetClientValidationRulesメソッド」って分かりづらい感ばりばりですが、今回は「userminor」というクライアントで使用する検証の名前とエラーメッセージを定義してるだけです。検証に別のパラメータ等を使用する場合はここで定義することができます(今回はややっこしくなるのでやめました)。次にモデルにこの属性を定義します。

Userモデルへの追加
public class User : IValidatableObject
{
 
    public int UserId { get; set; }
 
    [StringLength(50)]
    public string UserName { get; set; }
 
    [Column("Gender", TypeName = "int")]
    [EnumDataType(typeof(Genders))]
    public Genders Gender { get; set; }
 
    [UserMinor]
    public DateTime Birthday { get; set; }
 
    [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}", ErrorMessage = "アドレスが不正です。")]
    public string Email { get; set; }
 
    [Compare("Email", ErrorMessage = "入力されたアドレスと異なっています。")]
    public string CompareEmail { get; set; }
 
    [RegularExpression(@"[0-9]*$", ErrorMessage = "半角数値のみで入力して下さい。")]
    public string Tell { get; set; }
 
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        using (var context = new MyContext())
        {
            var overlap = context.Users.Where((p) => p.UserName == this.UserName).FirstOrDefault();
 
            if(overlap !=null)
            {
                yield return new ValidationResult("そのユーザー名は既に使われています。", new[] { "UserName" });
            }
        }
    }
}

 こっちは「Birthday」に「UserMinor」属性をつけただけでお終いです。ちなみにこのままで実行すると他の属性とは異なり、サーバーサイドでの検証は発生しますが、クライアントサイドでの検証は発生しません。一応、実行結果載せます。

実行結果(Post発生後)

image

 これをクライアントサイドでも検証が発生するようにスクリプトを定義します。これに関しては、はっきり言って2度手間です。

Editビューへの追加(一部抜粋)
<script type="text/javascript">
    $.validator.addMethod('userminor', function (value, element, params) {
        var today = new Date();
        var overYear = today.getFullYear() - 18;
        var overBirthday = new Date(overYear, today.getMonth(), today.getDay());
 
        if (!value) {
            return true;
        } else {
            if (overBirthday < Date.parse(value)) {
                return false;
            } else {
                return true;
            }
        }
    });
 
    $.validator.unobtrusive.adapters.addBool('userminor');
</script>

 サーバーサイドでの検証はValidateプラグインの「addMethod」関数とUnobtrusiveプラグインの「addBool」関数を利用します。前者で検証内容を定義し、後者で検証のパラメータ等を登録します。今回はパラメータがないので「addBool」関数を使用していますが、パラメータがある場合、別の関数を利用したり、独自に定義したりすることができます。以下、実行結果です。

実行結果(Post発生前)

image

 う~ん、何かここまですると面倒くさいから独自検証に関してはサーバーサイドだけでいいんじゃ?って気もしないでもないですね。まぁ、無駄なトラフィックを発生させたくなければって感じでしょうか?

 というわけで今回はここまで。







デル株式会社
pagetop