asp-validation-summary の動作を確認する

ページ更新日 :
ページ作成日 :

環境

Visual Studio
  • Visual Studio 2019
ASP.NET Core
  • 3.0
  • 3.1

はじめに

ASP.NET Core で入力値の検証エラーを画面に表示させる方法として、asp-validation-summary を使用する方法があるのですが、 指定できるパラメータの違いがよくわからなかったので調べてみました。

指定できる値については以下の3つがあります。

  • None
  • ModelOnly
  • All

これらの違いについてそれぞれどのように動作するか、調べてみた結果以下のようになりました。

事前準備

とりあえず動作を検証するために下準備を行います。 大部分については冗長的に作ってあるので、結果だけ見たい方は記事の後半をご覧ください。

今回の検証はフォームでユーザーを登録する想定をしていますので、ユーザーのビューモデルを作成します。 いろいろ項目を入れていますが、最低1つあれば検証はできるので中身はなんでも構いません。 DataAnnotation によるチェックを行うので Required や StringLength の属性を設定しておきます。 動作の違いを確認するために IsAccepted だけ余分に入れています。

** Models/UserViewModel **

using System;
using System.ComponentModel.DataAnnotations;

namespace ValidationSummaryTest.Models
{
  public class UserViewModel
  {
    [Required]
    [StringLength(20)]
    public string ID { get; set; }

    [Required]
    [StringLength(50)]
    public string Name { get; set; }

    // 省略

    [Required]
    public bool IsAccepted { get; set; }
  }
}

asp-validation-summary の3つの値を調べるため、それぞれのアクションを作ります。 とはいっても asp-validation-summary はビュー側の話なのでそれぞれのアクションの中身はメッセージ以外はすべて同じコードです。

IsAccepted にチェックがついていない場合はエラーを追加しています。 エラーにはモデルとは異なるキーの指定と空のキーを指定したエラーを追加しています。

エラーがなければ正常登録のメッセージを表示させます。

HomeController.cs

public IActionResult ValidateNone() => View();

[HttpPost]
public IActionResult ValidateNone(UserViewModel model)
{
  if (model.IsAccepted == false)
  {
    ModelState.AddModelError("PropertyName1", "プロパティに依存するエラー (None)");
    ModelState.AddModelError("", "空のキーエラー (None)");
  }
  if (ModelState.IsValid == false) return View(model);
  ViewData["Message"] = "正常に登録しました。";
  return View(model);
}

public IActionResult ValidateModelOnly() => View();

[HttpPost]
public IActionResult ValidateModelOnly(UserViewModel model)
{
  if (model.IsAccepted == false)
  {
    ModelState.AddModelError("PropertyName1", "プロパティに依存するエラー (ModelOnly)");
    ModelState.AddModelError("", "空のキーエラー (ModelOnly)");
  }
  if (ModelState.IsValid == false) return View(model);
  ViewData["Message"] = "正常に登録しました。";
  return View(model);
}

public IActionResult ValidateAll() => View();

[HttpPost]
public IActionResult ValidateAll(UserViewModel model)
{
  if (model.IsAccepted == false)
  {
    ModelState.AddModelError("PropertyName1", "プロパティに依存するエラー (All)");
    ModelState.AddModelError("", "空のキーエラー (All)");
  }
  if (ModelState.IsValid == false) return View(model);
  ViewData["Message"] = "正常に登録しました。";
  return View(model);
}

index.cshtml には各テストを行うページへのリンクを追加しています。

index.cshtml

@{
    ViewData["Title"] = "Home Page";
}

<div class="text-center">
  <h1 class="display-4">Welcome</h1>
  <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

<ul>
  <li><a asp-action="ValidateNone">ValidateNone</a></li>
  <li><a asp-action="ValidateModelOnly">ValidateModelOnly</a></li>
  <li><a asp-action="ValidateAll">ValidateAll</a></li>
</ul>

検証用のユーザー登録ページです。 エラーがあった場合に内容を表示させるパラメータとして asp-validation-summary に None を指定しています。 また、デフォルトのエラーメッセージが表示されるかどうかを確認するために div タグ内に「エラー直接入力」の文章を入れています。

他のコードについてはモデルをベースにスキャフォールディングで自動生成させたコードなので詳しくは説明しません。 (一部手で修正しているところがありますが、今回の検証とは関係ありません)

各入力項目(input タグ)のそばに asp-validation-for 属性が設定されている span タグがありますが、 これは各々の入力項目のエラーメッセージを表示させるためのタグ(属性)であり、 asp-validation-summary に指定したパラメータとは無関係に、エラーがあれば必ずその場所にエラーメッセージが表示されます。

あと、asp-validation-summary はサーバーの検証結果が表示されるので、クライアントの検証処理(_ValidationScriptsPartial)はコメントアウトしています。

ValidateNone.cshtml

@model ValidationSummaryTest.Models.UserViewModel

@{
  ViewData["Title"] = "ValidateNone";
}

<h1>ValidateNone</h1>

<h4>UserViewModel</h4>
<hr />
<div class="row">
  <div class="col-md-4">
    <form asp-action="ValidateNone">
    
      <div asp-validation-summary="None" class="text-danger">エラー直接入力</div>
      
      <div class="form-group">
        <label asp-for="ID" class="control-label"></label>
        <input asp-for="ID" class="form-control" />
        <span asp-validation-for="ID" class="text-danger"></span>
      </div>
      <div class="form-group">
        <label asp-for="Name" class="control-label"></label>
        <input asp-for="Name" class="form-control" />
        <span asp-validation-for="Name" class="text-danger"></span>
      </div>
      
      @* 省略 *@
      
      <div class="form-group form-check">
        <label class="form-check-label">
          <input class="form-check-input" asp-for="IsAccepted" /> @Html.DisplayNameFor(model => model.IsAccepted)
        </label>
      </div>
      <div class="form-group">
        <input type="submit" value="Create" class="btn btn-primary" />
      </div>
    </form>
  </div>
</div>

<div>
  <a asp-action="Index">Back to List</a>
</div>

@* サーバーのエラーを表示したいのでコメントアウト *@
@*@section Scripts {
  @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}*@

ValidateModelOnly.cshtml のページは asp-validation-summary に ModelOnly を指定しているところ以外は 表示テキストを覗いて ValidateNone.cshtml と同じです。

ValidateModelOnly.cshtml

@model ValidationSummaryTest.Models.UserViewModel

@{
  ViewData["Title"] = "ValidateModelOnly";
}

<h1>ValidateModelOnly</h1>

<h4>UserViewModel</h4>
<hr />
<div class="row">
  <div class="col-md-4">
    <form asp-action="ValidateModelOnly">
      <div asp-validation-summary="ModelOnly" class="text-danger">エラー直接入力</div>
      
      @* 省略 *@

    </form>
  </div>
</div>

@* 省略 *@

ValidateAll.cshtml のページも asp-validation-summary に All を指定しているところ以外は他とほぼ同じです。

ValidateAll.cshtml

@model ValidationSummaryTest.Models.UserViewModel

@{
  ViewData["Title"] = "ValidateAll";
}

<h1>ValidateAll</h1>

<h4>UserViewModel</h4>
<hr />
<div class="row">
  <div class="col-md-4">
    <form asp-action="ValidateAll">
      <div asp-validation-summary="All" class="text-danger">エラー直接入力</div>
    </form>

    @* 省略 *@
  </div>
</div>

@* 省略 *@

動作確認

None

プログラムを作成したら動作確認をしてみます。ますは asp-validation-summary=None の画面です。

初期表示の状態で「エラー直接入力」と表示されているので、既定のエラーメッセージとしては使えないようです。 Required を設定しているパラメータがあるのでこのまま更新処理を実行してみます。

None の名前の通り、エラーがあっても何も表示されません。 既定のエラーメッセージもそのまま残っているので、直接 None を設定して使う機会はなさそうです。 使う機会があるとすれば、asp-validation-summary に設定する値を動的にして、エラーを表示しない場合は None にするような場面でしょうか。

ModelOnly

asp-validation-summary=ModelOnly を指定した画面です。 検証エラーのサンプルやスキャフォールディングでも出てくるので一番利用場面が多いと思います。

div タグ内に入れていた「エラー直接入力」のテキストは表示されていないので、入れておいても問題なさそうです。

登録処理を行いエラーを表示させてみた状態です。 ここで表示されるメッセージは、div タグ内にあらかじめ入れていた既定のメッセージとサーバー側で発生させたエラーメッセージのみになります。 ただし、サーバー側で空キーのメッセージをいれていない場合は div タグ内のメッセージも出てきませんので注意してください。

サーバー側では2つのエラーメッセージを入れていますが、表示されているのはキーに空文字を入れたエラーのみとなっています。 キーを指定した場合はその名前を持つモデルのプロパティのエラーとして扱われるので、asp-validation-summary の箇所には出てきません。 代わりに同名の asp-validation-for 属性を指定したタグがあればその個所にメッセージが表示されます。

All

asp-validation-summary=All を指定した画面です。

なぜか埋め込んだ「エラー直接入力」のテキストが表示されています。ModelOnly だけ表示されない理由はよくわかりません。 とりあえず All では埋め込みは使えないようです。

そのまま更新処理を行った後の画面です。 全ての入力項目(プロパティ)のエラーメッセージが一覧で表示されます。 各入力項目にもエラーメッセージが表示されるので、もしエラーを纏めて表示させるために All を指定するのであれば、 各入力項目に設定している asp-validation-for 属性は外した方がいいでしょう。

サーバー側で追加したエラーメッセージも表示されています。キーを指定したエラーも表示されていることが確認できます。

まとめ

asp-validation-summary には3つのパラメータがありましたが、 エラーの箇所の分かりやすさや、重複などを省く意味合いでは ModelOnly を使用するのがよいでしょう。 注意点としては asp-validation-summary に表示されるメッセージは空のキーを指定したメッセージのみとなるところです。

ちなみに空のキーには何個でもメッセージは追加できるので、複数のメッセージを表示させるために文字列を結合する必要はありません。

All についてはエラーメッセージを1か所に集めたい場合に使用する、 None についてはなんらかの理由でエラーメッセージを表示させたくないタイミングがあった場合に動的に設定する、 という目的で使用するのがよいでしょう。