WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

ASP.NET Core MVC 検証属性の自作

by WebSurfer 3. February 2020 14:52

@IT の連載に「自作の検証属性を定義する(クライアントサイド編)」という .NET Framework MVC アプリにクライアント側で JavaScript による検証を含めて検証属性を自作する記事があります。ASP.NET Core 3.1 MVC ではそれと同等の検証属性をどのように実装するかという話です。

Custom Validator

(注:プロパティにまたがる検証を行うための CustomValidationAttribute の話ではなく、一つのフィールドの内容を独自の条件で検証するためのものです)

.NET Framework MVC との違いは IClientValidatable の代わりに IClientModelValidator インターフェイスを継承し AddValidation メソッドを実装するところです。

Model、検証属性の定義、クライアント側での検証用 JavaScript のコードを以下にアップしておきます。注意点はそれらに書きましたので 見てください。

Model

Model は .NET Framework MVC のものと全く同じです。自作の検証属性 InArrayAttribute は Publish プロパティに付与されています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System.Globalization;

namespace MvcCoreApp.Models
{
  public class Book
  {
    [Key]
    [Display(Name = "ISBNコード")]
    [Required(ErrorMessage = "{0}は必須です。")]
    [RegularExpression(
      "[0-9]{3}-[0-9]{1}-[0-9]{3,5}-[0-9]{3,5}-[0-9A-Z]{1}",
      ErrorMessage = "{0}はISBNの形式で入力してください。")]
    public string Isbn { get; set; }

    [Display(Name = "書名")]
    [Required(ErrorMessage = "{0}は必須です。")]
    [StringLength(100, 
      ErrorMessage = "{0}は{1}文字以内で入力してください。")]
    public string Title { get; set; }

    [Display(Name = "価格")]
    [Range(100, 10000, 
      ErrorMessage = "{0}は{1}~{2}の間で入力してください。")]
    public int? Price { get; set; }

    [Display(Name = "出版社")]
    [StringLength(30, 
      ErrorMessage = "{0}は{1}文字以内で入力してください。")]
    [InArray("翔泳社,技術評論社,秀和システム," + 
      "毎日コミュニケーションズ,日経BP社,インプレスジャパン")]
    public string Publish { get; set; }

    [Display(Name = "刊行日")]
    [Required(ErrorMessage = "{0}は必須です。")]
    public DateTime Published { get; set; }
  }
}

自作の検証属性

出版社のリストとの比較検証を行う InArrayAttribute を定義します。IClientModelValidator を継承しているところと、AddValidation メソッドの実装に注目してください。(ちなみに .NET Framework MVC では IClientValidatable インターフェイスの GetClientValidationRules メソッドを使います)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System.Globalization;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;

namespace MvcCoreApp.Models
{
  [AttributeUsage(AttributeTargets.Property, 
                  AllowMultiple = false)]
  public class InArrayAttribute : 
               ValidationAttribute, IClientModelValidator
  {
    // 値リストを表すプライベート変数
    private string _opts;

    // コンストラクタ(値リストとエラーメッセージを設定)
    public InArrayAttribute(string opts)
    {
      this._opts = opts;
      this.ErrorMessage = 
            "{0} は「{1}」のいずれかで指定してください。";
    }

    // プロパティの表示名と値リストでエラーメッセージ作成
    public override string FormatErrorMessage(string name)
    {
      return String.Format(CultureInfo.CurrentCulture, 
                           ErrorMessageString, name, _opts);
    }

    // サーバー側での検証
    // 値リストに入力値が含まれているかをチェック
    public override bool IsValid(object value)
    {
      // 入力値が空の場合は検証をスキップ 
      if (value == null) { return true; }

      // カンマ区切りテキストを分解し、入力値valueと比較 
      if (Array.IndexOf(_opts.Split(','), value) == -1)
      {
        return false;
      }
      return true;
    }

    // IClientModelValidator が実装するメソッド。
    // 検証対象の html 要素 (input) に控えめな JavaScript
    // による検証のための属性 (data-val 他) と値を追加
    public void AddValidation(
            ClientModelValidationContext context)
    {
      MergeAttribute(context.Attributes, "data-val", "true");
      var errorMessage = FormatErrorMessage(
          context.ModelMetadata.GetDisplayName());
      MergeAttribute(context.Attributes, 
                     "data-val-inarray", errorMessage);
      MergeAttribute(context.Attributes, 
                     "data-val-inarray-opts", this._opts);
    }

    // 上の AddValidation メソッドで使うヘルパーメソッド
    private bool MergeAttribute(
                  IDictionary<string, string> attributes, 
                  string key, string value)
    {
      if (attributes.ContainsKey(key))
      {
        return false;
      }
      attributes.Add(key, value);
      return true;
    }
  }
}

検証用 JavaScript(View に組み込み)

View にインラインで書いていますがコード自体は @IT の記事のものと同じです。検証用 JavaScript ライブラリに自作の検証スクリプトをどのように追加し、連携を取って動くようにするかという点がキモです。後者については、Unobtrusive Client Validation in ASP.NET MVC 3 という記事の中の Single value validators というセクションに記述がありました。正直読んでもよく分からなかったですが。(汗)

@model MvcCoreApp.Models.Book

・・・中略・・・

<div class="form-group">
  <label asp-for="Publish" class="control-label"></label>
  <input asp-for="Publish" class="form-control" />
  <span asp-validation-for="Publish" class="text-danger">
  </span>
</div>

・・・中略・・・

@section Scripts {
  @{
    await 
    Html.RenderPartialAsync("_ValidationScriptsPartial");
  }

  <script type="text/javascript">
    $.validator.addMethod("inarray",
      function (value, element, parameters) {
        // 入力値が空の場合は検証をスキップ
        value = $.trim(value);
        if (value === '') {
          return true;
        }

        // カンマ区切りテキストを分解し、入力値valueと比較
        if ($.inArray(value, parameters.split(',')) === -1) {
          return false;
        }
        return true;
      });

    $.validator.unobtrusive.adapters.
                addSingleVal('inarray', 'opts');
  </script>
}

Tags: , , ,

CORE

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  February 2020  >>
MoTuWeThFrSaSu
272829303112
3456789
10111213141516
17181920212223
2425262728291
2345678

View posts in large calendar