WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

MVC5 のエラーメッセージ表示

by WebSurfer 2019年2月15日 13:35

Visual Studio Community 2015 で作成した MVC5 アプリでは、スキャフォールディング機能を使って自動生成した View で検証エラーとなった場合のエラーメッセージは、デフォルトでは以下のように表示されます。

検証エラーメッセージ

(注:画像の「ValidationSummary に表示するために追加。」はアクションメソッドで追加した model-level エラーです。また価格のテキストボックス下の「入力形式が正しくありません。」は、デフォルトでは「The value 'zzz' is not valid for 価格.」となるのを書き換えています。そのあたりのことは別の記事「int 型プロパティの検証、エラーメッセージ」に書きましたので見てください)

それを、先の記事「DataType 属性による検証」の画像(Visual Studio 2010 で作った MVC4 アプリ)のように表示するにはどうしたらよいかを書きます。

上の画像のように表示される理由は、スキャフォールディングで自動生成される View のコードで、ValidationSummary メソッドの第 1、第 2 引数と、ValidationMessageFor メソッドの第 2 引数が以下のように設定されているためです。

ValidationSummary(true, "", 
    new { @class = "text-danger" })

ValidationMessageFor(model => model.ID, "", 
    new { @class = "text-danger" })

それらを以下のように変更すれば、設定は(あくまで「設定」だけです)先の記事「DataType 属性による検証」の MVC4 アプリのものと同じになりますが、表示はそのようにはなりません。

ValidationSummary(false, "以下のエラーを修正してください。", 
    new { @class = "text-danger" })

ValidationMessageFor(model => model.ID, "*", 
    new { @class = "text-danger" })

以下の画像のように、初期画面や検証エラーのない時も ValidationSummary に「以下のエラーを修正してください。」と、各テキストボックスに「*」が表示されてしまいます。

検証エラーメッセージ

また、あるテキストボックスへのユーザー入力の検証エラーが発生した場合も当該テキストボックスの枠の色は赤に変わりません。

その理由は、Visual Studio 2010 で作った MVC4 アプリの site.css にある以下のクラス定義が、Visual Studio 2015 で作った NVC5 アプリの site.css には無いからです。

/* styles for validation helpers */
.field-validation-error {
    color: #e80c4d;
    font-weight: bold;
}

.field-validation-valid {
    display: none;
}

input.input-validation-error {
    border: 1px solid #e80c4d;
}

input[type="checkbox"].input-validation-error {
    border: 0 none;
}

.validation-summary-errors {
    color: #e80c4d;
    font-weight: bold;
    font-size: 1.1em;
}

.validation-summary-valid {
    display: none;
}

MVC4 も MVC5 も、検証結果に応じて、表示される html 要素に付与される class 属性の設定が変わります。

ValidationSummary は validation-summary-valid と validation-summary-errors、ValidationMessageFor は field-validation-valid と field-validation-error、TextBoxFor / EditorFor は valid と input-validation-error というように。

なので、MVC5 の css に上記クラス定義を追加すれば表示は MVC4 と同じになります。

Tags: ,

Validation

MVC5 でエラーメッセージが英語

by WebSurfer 2019年2月6日 12:48

Visual Studio Community 2015 のテンプレートで [MVC] を選択して生成した ASP.NET MVC5 アプリケーションでは、そのままではデフォルトで表示されるエラーメッセージが英語になってしまいます。その理由と日本語にする方法を書きます。

エラーメッセージ

上の画像を表示したコードは先の記事「日付と通貨の書式設定」と同じものです(ただし、View から DisplayFor は削除しています)。

一番上のテキストボックスに "1c" という数字としては無効な文字列を入力するとクライアント側の検証で NG となり、"The field ID must be a number." という英語のエラーメッセージが表示されます。(IE11 の場合です。動作はブラウザによって異なります)

Model のプロパティに付与したデータアノテーション属性には、先の記事に書いコードの通り、そのようなエラーメッセージは設定していませんが、html ソースには以下のように data-val-number="The field ID must be a number." という属性が設定されます。上の画像ではその設定値が表示されています。

<input class="form-control text-box single-line" 
    data-val="true" 
    data-val-number="The field ID must be a number." 
    data-val-regex="数字 1 ~ 5 文字" 
    data-val-regex-pattern="^\d{1,5}$" 
    data-val-required="ID は必須" 
    id="ID" name="ID" 
    type="number" 
    value="1" />

なぜ "The field ID must be a number." というような英語になってしまうかと言うと、日本語のサテライトアセンブリ System.Web.Mvc.resources.dll がインストールされてないからです。

(ちなみに、Visual Studio 2010 のテンプレートで作る MVC4 は自動的に日本語のサテライトアセンブリ System.Web.Mvc.resources.dll が bin/ja フォルダに配置されます。MVC3 も、仕組みは不明ですが、エラーメッセージは日本語になります)

解決するには、NuGet パッケージ管理で Microsoft.AspNer.Mvc.ja をインストールしてやるのが簡単かつ確実だと思います。以下の画像を見てください。

NuGet パッケージ追加

それにより、プロジェクトの bin フォルダに ja フォルダが追加され、その中に日本語のサテライトアセンブリが追加されます。

日本語のサテライトアセンブリ

上の画像の ja フォルダの中の EntityFramework 関係のアセンブリは NuGet パッケージ Microsoft.AspNer.Mvc.ja をインストールする際に System.Web.Mvc.resources.dll と同時にインストールされたものです。(依存関係は不明)

インストール済みの他のパッケージが Microsoft.AspNet.Mvc.ja に依存し、更新が必要な場合は同時に更新されます。ちなみに、自分の環境で v5.2.7 をインストールしたら、Microsoft.AspNet.Mvc.5.2.3 ⇒ 5.2.7、Microsoft.AspNet.Razor.3.2.3 ⇒ 3.2.7、Microsoft.AspNet.WebPages.3.2.3 ⇒ 3.2.7 というように更新されました。

【注意】

日本語のサテライトアセンブリをインストールしたら、web.config の system.web 要素内に globalization 要素を追加し、その Culture, UICulture 属性を "auto" に設定するのを忘れないようにしてください。

Culture, UICulture を "auto" に設定すると、ASP.NET は、ブラウザから送信されてくる要求ヘッダに含まれる Accept-Language の設定を調べて、その要求を処理するスレッドのカルチャを Accept-Language に設定されているカルチャに書き換えてくれます。

そして、実行時に、リソースマネージャが現在の要求を処理しているスレッドのカルチャ情報を参照してローカライズされたリソースを検索し、UI に表示されるテキストを取得するという仕組みになっています。

Culture, UICulture を "auto" に設定するのを忘れるとブラウザの言語設定は無視されます。デフォルトではシステムのロケールに該当するカルチャがスレッドに設定されます。

なので、ホスティングサービス(Azure も含む)などでサーバーの OS が英語版だったりすると ja フォルダにある日本語のサテライトアセンブリは使われず、元の英語に戻ってしまうはずです。

Tags: ,

Validation

ASP.NET MVC 用の CompareValidator

by WebSurfer 2018年12月31日 12:06

ASP.NET Web Forms には CompareValidator という検証コントロールがあって、2 つの TextBox に入力された値の大小比較をして検証できる機能があります。

それと同様な検証機能を ASP.NET MVC で実装する方法を書きます。(元の話は Teratail のスレッド ASP.NET MVC4 TimeSpan型入力値の検証です)(以下の記事は .NET Framework 版の MVC5 での例ですが、ASP.NET Core 3.1 MVC の場合も Model と検証属性のコードは同じです)

MVC の CompareValidator

ASP.NET MVC には CompareAttribute という検証用のデータアノテーション属性がありますが、それには大小を比較する機能はなく、同じか否かを検証するのみです。

なので、例えば、ユーザーが開始時間と終了時間をテキストボックスに入力した際に、終了時間が開始時間より後になっていることを検証するような場合は、カスタム検証機能を実装することになります。

複数のプロパティにまたがった検証をする場合、Teratail のスレッド「MVC モデルのバリデーションについて」の回答に書いたように、CustomValidationAttribute を使うか、モデルに IValidatableObject インターフェイスを実装するという手段を使うのが普通のようです。

しかし、2 つのテキストボックス入力の大小比較程度なら、CustomValidationAttribute を使わなくても、リフレクションを使って比較対象のプロパティの値を取得し、それと自身のプロパティの値を比較して検証結果を返すということができます。

参考にしたのは Compare Validator in MVC という記事の Limitations of the Compare DataAnnotation 以下のセクションです。

上の画像を表示するのに使ったサンプルコードは以下の通りです。TimeSpan 型のプロパティを検証の対象としていますが、DataTime 型その他でもキャストを変更して同様に検証可能です。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Globalization;

namespace Mvc5App.Models
{
  public class KintaiModel
  {
    public IList<Record> Records { get; set; }
  }

  public class Record
  {
    public int Id { set; get; }
    public string Week { set; get; }
    public TimeSpan Open { set; get; }

    [TimeGreaterThan("Open", 
                   "{0} は Open より後に設定してください")]
    public TimeSpan Close { set; get; }

    // ・・・中略・・・
  }

  [AttributeUsage(AttributeTargets.Property)]
  public class TimeGreaterThanAttribute : ValidationAttribute
  {
    private string _startTimePropertyName;

    // コンストラクタ
    public TimeGreaterThanAttribute(
            string startTimePropertyName, string errorMsg)
    {
      this._startTimePropertyName = startTimePropertyName;
      this.ErrorMessage = errorMsg;
    }

    public override string FormatErrorMessage(string name)
    {
      return string.Format(CultureInfo.CurrentCulture,
                           ErrorMessageString, name);
    }

    protected override ValidationResult IsValid (
        object value, ValidationContext validationContext)
    {
      // ObjectType はこの属性を付与したプロパティが属する
      // クラス、即ち上のコードの Record クラスとなる
      System.Reflection.PropertyInfo propertyInfo = 
          validationContext.ObjectType.
          GetProperty(this._startTimePropertyName);
      object propertyValue = 
          propertyInfo.
          GetValue(validationContext.ObjectInstance, null);

      if ((TimeSpan)value > (TimeSpan)propertyValue)
      {
        return ValidationResult.Success;
      }
      else
      {
        // ValidationResult の引数に null または "" を設定する
        // と FormatErrorMessage メソッドの戻り値が設定される
        return new ValidationResult(null);
      }
    }
  }
}

上の画像・コードではクラスのコレクションのモデルバインディングを行っていますが、そのような場合でもリフレクションを使って比較対象のクラスのプロパティを特定して検証可能です。ただ、リフレクションを使うということで、性能的にどうかという懸念は消せてません。

というわけで後で考えて気が付いたのですが、上ような例の場合はリフレクションを使わなくても、IsValid メソッドの中のコードを以下のようにして可能でした。

var model = validationContext.ObjectInstance as Record;
if (model == null)
{
    throw new NullReferenceException();
}

if ((TimeSpan)value > model.Open)
{
    return ValidationResult.Success;
}
else
{
    return new ValidationResult(null);
}

また、上のサンプルコードはサーバー側だけの検証で、クライアント側での JavaScript / jQuery による検証はかかりません。

単一のプロパティのカスタム検証であれば、@IT の記事の「第4回 検証属性の自作とクラス・レベルのモデル検証」のセクションに書かれているようにして、サーバー側とクライアント側両方の検証機能を実装できます。

しかし、今回のケースのように 2 つのプロパティにまたがった検証をする場合、スクリプトを ASP.NET MVC の検証システムの中に無理なく不整合なく取り込む具体例は少なくとも自分は見たことがないです。(自分が知らないだけだという可能性は否定しきれませんが)

Tags: ,

Validation

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar