WebSurfer's Home

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

DataType 属性による検証

by WebSurfer 2016年3月8日 17:21

データアノテーション検証でのデフォルトのエラーメッセージと DataType 属性による検証についておぼえておいた方がよさそうなことを備忘録として書きます。

エラーメッセージ

上の画像は、下のサンプルコードの Model のパブリックプロパティに付与したデータアノテーション属性によるエラーメッセージです。

(注1:上に表示されているエラーメッセージは全てクライアント側での検証結果によるものです。)

(注2:BrithDate については 0001 で何故かクライアント側での検証はパスします。ただし、サーバー側で検証がかかって、コントローラーで ModelState.IsValid は false になります。エラーメッセージは上記と違って「値 '0001' は BirthDate に対して無効です」となります)

なお、自分が検証に使った環境は Vista SP2 32-bit, .NET 4, Visual Studio 2010 プロフェッショナル, MVC4 のインターネットアプリケーションのテンプレート使用、ASP.NET 開発サーバー利用、IE9 です。

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

namespace Mvc4App2.Models
{
  public class DefaultErrorMessage
  {
    [Required]
    public string Name { get; set; }

    [Required]
    [DataType(DataType.Password)]
    [RegularExpression(@"^(?=.*\d)(?=.*[A-Z])[A-Z0-9]{4,8}$")]
    public string PassWord { get; set; }

    [DataType(DataType.EmailAddress)]
    public string Email { get; set; }

    [DataType(DataType.PhoneNumber)]
    public string Phone { get; set; }

    [DataType(DataType.Date)]
    public DateTime BirthDate { get; set; }

    [DataType(DataType.Url)]
    public string Url { get; set; }

    [StringLength(10)]
    [DataType(DataType.MultilineText)]
    public string Message { get; set; }
  }
}

各属性の ErrorMessage プロパティに何も設定しなくても上の画像のようにデフォルトでエラーメッセージが出ます。

しかし、正規表現がそのまま出たり(「フィールド PassWord は正規表現 '^(?=.*\d)(?=.*[A-Z])[A-Z0-9]{4,8}$' と一致する必要があります。」とか言われても一般ユーザーには訳がわからない)、一部はローカル化されておらず英文のままだったりとユーザーフレンドリーとは言えないようです。

さらに、MSDN フォーラムのスレッドASP.NET MVC5 でVaridateメッセージが英語で初めて知りましたが、自動的にはローカル化されず全部英語になってしまうこともあるようです。

また、DataType 属性については、付与しても検証は行われずエラーメッセージは出ないものがありますので注意が必要です。上の例では Password, PhoneNumber, MultilineText では検証が行われていません。

Password と MultilineText にはエラーメッセージが出ていますが、それは DataType 属性によるものではなく、追加で付与した RegularExpression 属性と StringLength 属性によるものです。

一方、EmailAddress, Date, Url は検証がかかります。クライアント側での検証はブラウザ依存になると思いますので自分が試した IE9 以外で同じ結果になるかは分かりませんが(未検証・未確認です)。なので、DataType 属性による検証に頼らず、自分で StringLength 属性や RegularExpression 属性等を使ってきちんと検証したほうがよさそうです。

ただし、DataType 属性を付与することによるメリットは検証以外にもあります。それは、View のコードに全て Html.EditorFor を利用しても、DataType 属性によって生成される html ソースの input 要素の type 属性が変わってくる(MultilineText の場合は input 要素でなく、textarea 要素になる)ことです。ちなみに、上の Model の DataType 属性の設定では以下のようになります。

<input ... name="Name" type="text" value="" />
            
<input ... name="PassWord" type="password" value="" />
            
<input ... name="Email" type="email" value="" />
            
<input ... name="Phone" type="tel" value="" />
            
<input ... name="BirthDate" type="date" value="0001/01/01" />
            
<input ... name="Url" type="url" value="" />

<textarea ... id="Message" name="Message"></textarea>

それゆえ、Password と MultilineText にいては DataType 属性を付与するのがよさそうです。他のデータアノテーション属性による検証とバッティングすることもなさそうですし。

以上のような訳で、DataType 属性による検証やデフォルトのエラーメッセージでは期待した結果を得るのは難しいので、

  1. DataType 属性による検証は利用せず Range, RegularExpression, Required, StringLength 属性などを組み合わせて使う。
  2. DataType 属性を使用するのは、Password や MultilineText のように、レンダリングされる html ソースの input 要素の type 属性を "password" にしたいとか、input 要素でなく textarea 要素にしたい場合に限る。
  3. エラーメッセージは自分で各データアノテーション属性の ErrorMessage プロパティに設定する。(先の記事「コレクションのデータアノテーション検証」参照)
  4. Model のコード内にエラーメッセージをハードコーディングしたくない場合は自分でリソースファイル (.resx) を作って使う。(先の記事「データアノテーション検証の多言語対応」参照)
  5. エラーメッセージを多言語化する場合は自分でリソースファイルを追加してサテライトアセンブリを作って使う。(同じく、先の記事「データアノテーション検証の多言語対応」参照)

というようにするのが正解だと思いました。

-------- 2016/6/17 追記 --------

ErrorMessage プロパティの設定は行わず、デフォルトの日本語のエラーメッセージを利用したいのであれば、Azure など特にサーバーが外国にある場合は Culture, UICulture を "auto" に設定するのを忘れないようにしてください。

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

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

Culture, UICulture を "auto" に設定するのを忘れるとブラウザの言語設定は無視されます。デフォルトではシステムのロケールに該当するカルチャがスレッドに設定されますので、例えば英語 OS では英語のリソースから UI に表示されるテキストを取得します。(日本語のリソースがあっても使われません)

Tags: ,

MVC

クライアント側検証の無効化

by WebSurfer 2015年2月17日 18:40

先の記事 コレクションのデータアノテーション検証 でユーザー入力の検証の話を書きましたが、デフォルトで有効になっているクライアント側での検証を無効にするにはどうすればいいかという話を書きます。

ユーザー入力の検証

これは、Visual Studio 2010 で MVC4 アプリを インターネットアプリケーション テンプレートで作った場合の話で、Visual Studio 2013 とか MVC5 とかでは違うかもしれませんのでご注意ください。

アプリケーションルート直下の web.config での以下の定義がされており、クライアント側での検証がアプリケーション全体で有効に設定されています。

<appSettings>
  ・・・中略・・・
  <add key="ClientValidationEnabled" value="true" />
  ・・・中略・・・
</appSettings>

クライアント側での検証を無効にしたいことがあるとすると多分特定の View だけでしょうから、その場合は当該 View のコードに下記を追加すれば OK です。

@{
  Html.EnableClientValidation(false);
}

クライアント側検証には EnableClientValidation の他に EnableUnobtrusiveJavaScript が使われていますが、stackoverflow の記事 によると、後者は Ajax にも使われていて、false にすると Ajax が動かなくなるとのことなので注意してください。

以上で話は終わりなのですが、それだけでは記事としてはちょっと面白くないので、上記に関連して調べたことを備忘録として書いておきます。(笑)

MVC4 の新機能として JavaScript / CSS ファイルの縮小化と結合処置の自動化機能があり、その機能を利用するため、App_Start フォルダの BundleConfig.cs ファイルに以下の定義がされています。

public class BundleConfig
{
  public static void RegisterBundles(BundleCollection bundles)
  {
    bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                        "~/Scripts/jquery-{version}.js"));

    bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
                        "~/Scripts/jquery-ui-{version}.js"));

    bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                        "~/Scripts/jquery.unobtrusive*",
                        "~/Scripts/jquery.validate*"));

    // ・・・中略・・・
  }
}

アプリケーションルート直下の Scripts フォルダには jquery-1.7.1.js, jquery.validate.js その他必要な jQuery 関係の外部スクリプトファイルが自動的に配置されます。

Global.asax には以下のコードが記述されており、アプリケーション起動時に外部スクリプトファイルのパスが ASP.NET の BundleCollection オブジェクトに登録されるようになっています。(詳しくは @IT の記事 Visual Studio 2012の新機能とASP.NET 4.5のコア機能 (3/4) を見てください)

public class MvcApplication : System.Web.HttpApplication
{
  protected void Application_Start()
  {
      // ・・・中略・・・

      BundleConfig.RegisterBundles(BundleTable.Bundles);

      // ・・・中略・・・
  }
}    

BundleCollection オブジェクトに登録した外部スクリプトファイルを参照するには、View で Scripts.Render メソッド を使用します。

具体的には _Layout.cshtml(Web Forms アプリのマスターページに相当)の @Scripts.Render("~/bundles/jquery") と View の @Scripts.Render("~/bundles/jqueryval") で以下の外部スクリプトファイルへの参照が HTML ソースに定義されます。

<script src="/Scripts/jquery-1.7.1.js"></script>
<script src="/Scripts/jquery.unobtrusive-ajax.js"></script>
<script src="/Scripts/jquery.validate.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js"></script>

(注)上記は <compilation debug="true" ... /> の場合です。debug="false" の場合はファイルの縮小化と結合処置の自動化機能が働いて以下のようになります。

<script src="/bundles/jquery?v=1A_Q..."></script>
<script src="/bundles/jqueryval?v=-tc2Q..."></script>

以上ような設定において、例えば Model にアノテーション属性を以下のように付与したとします。

public class Parent
{
  //・・・中略・・・

  [Required(ErrorMessage = "{0} は必須")]
  [StringLength(5, ErrorMessage = "{0} は {1} 文字以内")]
  [Display(Name = "Parent Name")]
  public string Name { get; set; }

  //・・・中略・・・
}

そして、View のコードを以下のように記述したとします。

<div class="editor-label">
  @Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
  @Html.EditorFor(model => model.Name)
  @Html.ValidationMessageFor(model => model.Name)
</div>

そうすると、クライアント側検証が有効の場合(Html.EnableClientValidation(true) の時)は、ASP.NET がレンダリングする HTML ソースは以下のようになります。

<div class="editor-label">
  <label for="Name">Parent Name</label>
</div>
<div class="editor-field">
  <input 
    class="text-box single-line" 
    data-val="true" 
    data-val-length="Parent Name は 5 文字以内" 
    data-val-length-max="5" 
    data-val-required="Parent Name は必須" 
    id="Name" 
    name="Name" 
    type="text" 
    value="" />
  <span 
    class="field-validation-valid" 
    data-valmsg-for="Name" 
    data-valmsg-replace="true">
  </span>
</div>

上記のように input 要素に付与された data-val* 属性に従って、参照された外部スクリプトに含まれる Unobtrusive(控えめな)JavaScript がクライアント側での検証を行うようになります。

一方、クライアント側検証を無効にした場合(Html.EnableClientValidation(false) の時)は ASP.NET がレンダリングする HTML ソースは以下のようになります。(注:外部スクリプトファイルへの参照は変わりません)

<div class="editor-label">
  <label for="Name">Parent Name</label>
</div>
<div class="editor-field">
  <input 
    class="text-box single-line" 
    id="Name" 
    name="Name" 
    type="text" 
    value="" />
</div>

クライアント側での検証はかかりませんがサーバー側で検証は行われ、検証結果が NG の場合は input 要素の直後に以下の span 要素が追加され、エラーメッセージ(上の画像の赤い文字)が表示されます。

<span class="field-validation-error">
  Parent Name は必須
</span>

Tags: ,

MVC

データアノテーション検証の多言語対応

by WebSurfer 2014年9月11日 20:36

ASP.NET MVC Web アプリケーションのデータアノテーション属性を使用する際、リソースファイル(.resx)を使って表示されるメッセージを多言語化する方法を書きます。

エラーメッセージの多言語化

先の記事 コレクションのデータアノテーション検証 では、モデルのソースコードに中でメッセージをハードコーディングしました。

この記事では、メッセージをリソースファイルに格納し、そこからメッセージを取得して表示します。さらに、日本語と英語のリソースファイルを追加し、ブラウザの言語設定によって言語を切り替える例も紹介します。

さて、まずリソースファイルの格納場所ですが、どこが適当でしょうか?

ASP.NET には App_GlobalResources, App_LocalResources というリソースファイルを格納する専用のフォルダがあります。しかし、そのフォルダにリソースファイルを置くとユニットテストの際に問題があるそうです。(詳しくは Resource Files and ASP.NET MVC Projects の記事を参照)

ということで、この記事では App_GlobalResources フォルダは使わないで、とりあえずアプリケーションルート直下に直接置きました。(フォルダに入れる場合、名前空間にそのフォルダ名が追加になるので注意)

まず、Visual Studio のソリューションエクスプローラーを操作して[新しい項目の追加]ダイアログを表示し、[アセンブリ リソースファイル]を選んでリソースファイルを追加します。下の画像の例ではリソースファイル名を Resources1.resx という名前にしました。そうすると自動生成される Resource1.Designer.cs に定義される「厳密に型指定されたリソースクラス」において Resources1 がクラス名になります。

アセンブリ リソースファイルの追加

リソースファイルを追加したら、下の画像のように、Visual Studio の左側のウィンドウでその内容を設定します。表示するメッセージを[値]欄に、そのメッセージを取得するキー名を[名前]欄に記入します。ここで設定したものがデフォルトのメッセージとなります。

デフォルトのメッセージの設定

上の画像の例では、データアノテーション属性として DisplayAttribute, RequiredAttribute, StringLengthAttribute の 3 つを使用するという前提で、それぞれ Name, Required, StringLength というキー名(名前は任意です)でメッセージを設定しています。上のメッセージは説明用にテキトー書いたものです。実際にはデフォルトにふさわしいメッセージにしてください。

アクセス修飾子の設定を Public にするのを忘れないでください(上の画像で赤枠で囲んだ部分)。デフォルトでは Internal です。Public にするのを忘れると、後でユニットテストをする際に困ることになるはずです。(ちなみに、Internal ⇒ Public に変更すると、プロパティウィンドウで見て、カスタムツールが ResXFileCodeGenerator ⇒ PublicResXFileCodeGenerator に変わります)

Resources1.resx の設定が終わったら、日本語用に Resources1.ja-JP.resx、英語用に Resources1.en-US.resx という名前のリソースファイルを追加します。下の画像は日本語用の Resources1.ja-JP.resx の設定です。英語用も同様に(もちろんメッセージは英語で)設定します。

日本語リソースの設定

ja-JP, en-US というカルチャ名をリソースファイル名に追加するところがミソです。これによってブラウザの言語設定を ASP.NET が検出して(=要求ヘッダ情報を見て)自動的に使用するリソースファイルが切り替わります。

設定が完成すると以下の画像のようなリソースファイルができているはずです。(赤枠で囲った部分)

リソースファイル

次に、モデルでデータアノテーション属性を以下のように設定します。この例では Parent2 クラスの Name プロパティのみにリソースファイルからメッセージを取得して表示するようにしています。

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

namespace Mvc4App.Models
{
  public class Parent2
  {
    public Parent2()
    {
      Children = new List<Child2>();
    }

    public int Id { get; set; }
        
    [Required(
        ErrorMessageResourceType=typeof(Mvc4App.Resource1),
        ErrorMessageResourceName="Required")]
    [StringLength(
        5, 
        ErrorMessageResourceType = typeof(Mvc4App.Resource1), 
        ErrorMessageResourceName = "StringLength")]
    [Display(
        Name = "Name", 
        ResourceType = typeof(Mvc4App.Resource1))]
    public string Name { get; set; }

    public virtual IList<Child2> Children { get; set; }
  }

  public class Child2
  {
    public int Id { get; set; }
        
    [Required(ErrorMessage = "{0} は必須")]
    [StringLength(5, ErrorMessage = "{0} は {1} 文字以内")]
    [Display(Name = "Child Name")]
    public string Name { get; set; }

    public virtual Parent2 Parent { get; set; }
  }
}

RequiredAttribute, StringLengthAttirbute は ErrorMessageResourceType プロパティに Resource1.Designer.cs で定義される「厳密に型指定されたリソースクラス」の型を指定します(この例では typeof(Mvc4App.Resource1) です。Mvc4App が名前空間名、Resource1 がクラス名)。ErrorMessageResourceName プロパティにはリソースファイルに設定したキー名を設定します。

DisplayAttribute は、上の 2 つの属性とは設定するプロパティが異なり、ResourceType プロパティに「厳密に型指定されたリソースクラス」の型、Name プロパティにキー名を設定します。

最後に、web.config で globalization 要素の uiCulture, culture 属性を auto に設定します。これを忘れると、ブラウザの言語指定によるリソースファイルの自動切り替えは行われないので注意してください。(詳しくはこの記事の下の方の「2016/6/17 追記」を見てください)

<system.web>
  <globalization uiCulture="auto" culture="auto" />
</system.web>

これにより、例えば IE の言語の優先順位の設定を、下の画像のように日本語を最優先にしておくと、日本語のリソースファイル Resources1.ja-JP.resx からメッセージを取得して表示します。

IE の言語の優先順位の設定

その結果が一番上の画像です。

-------- 2016/6/17 追記 --------

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

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

Culture, UICulture を "auto" に設定するのを忘れるとブラウザの言語設定は無視されます。デフォルトではシステムのロケールに該当するカルチャがスレッドに設定されますので、例えば日本語 OS で xxx.ja-JP.resx というリソースがあれば、常にそれから UI に表示されるテキストを取得します。

Web サイトが日本語専用でサーバーも日本にあれば忘れても問題ないかもしれませんが、ホスティングサービス(Azure も含む)でサーバーが外国にある場合は Culture, UICulture を "auto" に設定するのを忘れると問題が出ると思います。

ロケール、カルチャ、Culture と UICulture の違いなどについては、以下の記事が参考になりましたので、忘れないようにリンクを張っておきます。

カルチャの基本とカルチャ情報

Tags: , ,

MVC

About this blog

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

Calendar

<<  2017年4月  >>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

View posts in large calendar