WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

CustomValidator のクライアント側での検証

by WebSurfer 27. June 2020 12:03

ASP.NET Web Forms アプリでユーザー入力の検証に用いられる CustomValidator のクライアント側での検証について調べたことをまとめて備忘録として書いておきます。

CustomValidator のクライアント側での検証

CustomValidator は、プログラマが独自の検証ロジックをコーディングして検証メソッドとしてページに実装し、ユーザー入力の検証を行うために利用されます。複数の入力コントロールにまたがって検証を行うことも可能です。

また、RegularExpressionValidator などと異なり、TextBox の他に CheckBox, RadioButton, DropDownList, FileUpload などのユーザー入力コントロールの検証に利用できます。

クライアント側での検証は、JavaScript を使って検証用メソッドを自力でコーディングし、それをページに実装することで可能になります(サーバー側でなければ検証できない場合は話は別です。ajax を使う手はいろいろ問題がありそうです。詳しくは先の記事「CustomValidator で jQuery.ajax 利用」を見てください)。

この記事の下の方に TextBox, CheckBox, RadioButton, DropDownList, FileUpload を対象として、CustomValidator によるクライアント側での検証を実装したサンプルコードを書いておきます。上の画像を表示したものです。

自分的に注意が必要と思う点を以下に箇条書きにしておきます。

  1. クライアント側での検証は html 要素の change イベントでかかるようになっています。CustomValidator を change イベントで動くようにするには ControlToValidate プロパティの設定が必要です。(注: submit でも検証がかかります。というか、change で検証がかかるのはユーザビリティ向上のためで、submit 時の検証がメインです)
  2. CheckBox, RadioButton コントロールに対しては CustomValidator の ControlToValidate プロパティを設定できません。設定すると HttpException がスローされ、例えば CheckBox の場合は「'CustomValidator' の ControlToValidate プロパティで参照されたコントロール 'CheckBox' を検証できません。」というエラーメッセージが表示されます。

    エラーとなる直接の理由は、ASP.NET 内部で CheckControlValidationProperty メソッドによる検証対象コントロールのチェックを行っていますが、CheckBox RadioButton コントロールには ValidationPropertyAttribute 属性が付与されてないためないためです。

    そもそもの理由は、Microsoft のドキュメントによると「ControlToValidate プロパティを設定せずに CustomValidator コントロールを使用することもできます。 これは、複数の入力コントロールを検証する場合や、CheckBox コントロールなどの検証コントロールで使用できない入力コントロールを検証する場合に一般的に行われます」とのことで、もともと CheckBox や RadioButton は検証コントロールを使う対象外のように読めます。
  3. TextBox, DropDownList, FileUpload コントロールについては、CustomValidator の ControlToValidate プロパティを検証対象コントロールの ID に設定すれば change イベントで検証がかかります。

    なお、ControlToValidate プロパティを設定しなくても submit で検証はかかりますので、change イベントでいちいち検証がかかるのは煩わしいという場合は設定しない方がよさそうです。(RequiredFieldValidator など他の検証コントロールは ControlToValidate プロパティを設定しないとエラーになりますので注意してください。CustomValidator だけ特別です)。
  4. どういう html 要素がどのタイミングで change イベントを発生させるかについては MDN の記事 HTMLElement: change event を見てください。その記事に書いてある通り、TextBox はユーザーが入力してフォーカスを外した時、DropDownList はユーザーが選択を変更したとき、FileUpload はユーザーがファイルを選択したとき change イベントが発生し、CustomValidator の ControlToValidate プロパティが設定されていれば検証がかかります。

    CheckBox (input type="checkbox"), RadioButton (input type="radio") も change イベントは発生しますが、上に述べたように CustomValidator の ControlToValidate プロパティを設定できないので、change イベントでは CustomValidator による検証はかかりません(submit で検証されます)。
  5. クライアント側での検証用 JavaScript のメソッドは CustomValidator の ClientValidationFunction プロパティに設定します。メソッド名が例えば Validate(sender, args) とすると、sender には CustomValidator が html に変換された span 要素が渡されます。args には IsValid, Value プロパティを持つ JavaScript オブジェクトが渡されます。

    CustomValidator の ControlToValidate プロパティが検証対象コントロールに対して設定してある場合は、args.Value には検証対象の入力コントロールが html に変換された input 要素の value 属性の値が渡されます。ControlToValidate プロパティが設定されてない場合は args.Value は空 "" になります。

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" 
    AutoEventWireup="true" CodeBehind="WebForm6.aspx.cs" 
    Inherits="WebApplication1.WebForm6" %>

<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" 
    runat="server">

    <script type="text/javascript">
        //<![CDATA[

        // ControlToValidate が設定されてない場合、引数 args には
        // テキストボックスの値が渡されないので注意。args に頼らず
        // 以下のようにしておくのがよさそう
        function TextBoxValidate(sender, args) {
            var tb =
                document.getElementById('<%= TextBox1.ClientID%>');

            var membership = tb.value.toLowerCase();
            if (membership === "gold" || membership === "silver") {
                args.IsValid = true;
            } else {
                args.IsValid = false;
            }
        }

        function CheckBoxValidate(sender, args) {
            var cb =
                document.getElementById('<%= CheckBox1.ClientID%>');

            if (cb.checked == true) {
                args.IsValid = false;
            } else {
                args.IsValid = true;
            }
        }

        function RadioButtonValidate(sender, args) {
            var rb =
                document.getElementById('<%= RadioButton1.ClientID%>');

            if (rb.checked == true) {
                args.IsValid = false;
            } else {
                args.IsValid = true;
            }
        }

        // ControlToValidate が設定されてない場合、引数 args には
        // テキストボックスの値が渡されないので注意。args に頼らず
        // 以下のようにしておくのがよさそう
        function DropDwonListValidate(sender, args) {
            var ddl =
                document.getElementById('<%= DropDownList1.ClientID%>');

           if (ddl.value == "2") {
                args.IsValid = false;
            } else {
                args.IsValid = true;
            }
        }

        function FileUploadValidate(sender, args) {
            if (window.File && window.FileList) {
                var fileUpload =
                    document.getElementById("<%=FileUpload1.ClientID%>");

                if (fileUpload.files[0] == null) {
                    args.IsValid = false;
                    return;
                }

                if (fileUpload.files[0].type != "image/jpeg") {
                    args.IsValid = false;
                    return;
                }


            } else {
                args.IsValid = true;
            }
            
        }

        //]]>
    </script>

</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" 
    runat="server">

    <h1>CustomValidator</h1>
    <p>CustomValidator のクライアント側での検証のタイミング</p>

    <table>
        <tr>
            <td>
                TextBox
            </td>
            <td>
                <asp:TextBox ID="TextBox1" runat="server">
                </asp:TextBox>
            </td>
            <td>
                <asp:CustomValidator ID="CustomValidator1" 
                    runat="server" 
                    ForeColor="Red"
                    Display="Dynamic"
                    ErrorMessage="Gold または Silver でない" 
                    ClientValidationFunction="TextBoxValidate" 
                    ControlToValidate="TextBox1">
                </asp:CustomValidator>
            </td>
        </tr>
        <tr>
            <td>
                CheckBox
            </td>
            <td>
                <asp:CheckBox ID="CheckBox1" runat="server" />
            </td>
            <td>
                <%--ControlToValidate="CheckBox1" を設定すると
                    HttpException がスローされる。理由は CheckBox
                    には ValidationProperty 属性が付与されてない
                    から。なので submit しないと検証はかからない
                    --%>
                <asp:CustomValidator ID="CustomValidator2" 
                    runat="server"
                    ForeColor="Red"
                    Display="Dynamic"
                    ErrorMessage="チェック不可" 
                    ClientValidationFunction="CheckBoxValidate">
                </asp:CustomValidator>
            </td>
        </tr>
        <tr>
            <td>
                RadioButton
            </td>
            <td>
                <asp:RadioButton ID="RadioButton1" runat="server" />
            </td>
            <td>
                <%--ControlToValidate="RadioButton1" を設定すると
                    HttpException がスローされる。理由は RadioButton
                    には ValidationProperty 属性が付与されてないから。
                    なので submit しないと検証はかからない
                    --%>
                <asp:CustomValidator ID="CustomValidator3" 
                    runat="server"
                    ForeColor="Red"
                    Display="Dynamic"
                    ErrorMessage="選択不可" 
                    ClientValidationFunction="RadioButtonValidate">
                </asp:CustomValidator>
            </td>
        </tr>
        <tr>
            <td>
                DropDownList
            </td>
            <td>
                <asp:DropDownList ID="DropDownList1" runat="server">
                    <asp:ListItem>0</asp:ListItem>
                    <asp:ListItem>1</asp:ListItem>
                    <asp:ListItem>2</asp:ListItem>
                </asp:DropDownList>
            </td>
            <td>
                <asp:CustomValidator ID="CustomValidator4" 
                    runat="server"
                    ForeColor="Red"
                    Display="Dynamic"
                    ErrorMessage="2 は選択不可" 
                    ClientValidationFunction="DropDwonListValidate" 
                    ControlToValidate="DropDownList1">
                </asp:CustomValidator>
            </td>
        </tr>
        <tr>
            <td>
                FileUpload
            </td>
            <td>
                <asp:FileUpload ID="FileUpload1" runat="server" />
            </td>
            <td>
                <asp:CustomValidator ID="CustomValidator5" 
                    runat="server"
                    ForeColor="Red"
                    Display="Dynamic"
                    ErrorMessage="jpg ファイル以外不可" 
                    ClientValidationFunction="FileUploadValidate" 
                    ControlToValidate="FileUpload1">
                </asp:CustomValidator>
            </td>
        </tr>
    </table>
    <asp:Button ID="Button1" runat="server" Text="Submit" />

</asp:Content>

上のコードはマスターページを利用しています。先の記事「ASP.NET 4.5 ScriptManager」で書きましたように、ASP.NET 4.5 以降でクライアントスクリプトを利用するサーバーコントロールが正しく機能するには、必要なクライアントスクリプトの ScriptManager への登録と全ページでの ScriptManager の配置が必要です。マスターページを使ってそのあたりを解決しています。

Tags: , , ,

Validation

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

by WebSurfer 15. February 2019 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 6. February 2019 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

About this blog

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

Calendar

<<  September 2020  >>
MoTuWeThFrSaSu
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar