.NET MVC Frameworkにおける属性チェックの基本と実装について

# 環境
- Visual Studio 2019
- .NET Framework 4.7.2
- .NET MVC 5.2.7.0

# 検証の為の属性の付与と、その概要
.NET MVCでは、以下のように[System.ComponentModel.DataAnnotations](https://docs.microsoft.com/ja-jp/dotnet/api/system.componentmodel.dataannotations?view=netframework-4.8) namespaceに属する各Attributeクラスのメソッドを"[]"で囲んだ記法によって、POST時に必須等の属性チェックを行うことができる。

※例えば、`[Required]`は上記namespaceのRequiredAttributeクラス（ValidationAttributeクラスを継承）に属している。

```c#:HelperViewModel.cs
using System.ComponentModel.DataAnnotations;

namespace HtmlHelperTest.ViewModels
{
    public class HelperViewModel
    {
        [Required(ErrorMessage = "{0}は必須です。")]
        [StringLength(10, ErrorMessage = "{0}は{1}文字以内で入力してください。")]
        [Display(Name = "サンプルプロパティ")]
        public string TmpProperty { get; set; }
    }
}
```

個別の属性については説明を省くが、上記のような属性を付与した状態でPOSTすると、`@Html.ValidationMessageFor()`を記述した箇所に以下のようにエラーメッセージをレンダリングする。
※個別の属性については[System.ComponentModel.DataAnnotations](https://docs.microsoft.com/ja-jp/dotnet/api/system.componentmodel.dataannotations?view=netframework-4.8) namespaceを参照

　`POST前`
<img width="450" alt="キャプチャ.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403060/902f357d-19ac-5ba5-f31a-1a716f7cbda0.png">

　`POST後　必須チェックエラー`
<img width="450" alt="キャプチャ2.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403060/516d1bf3-e0a4-bb84-ddcb-36641a8cfb3a.png">

　`POST後　桁数チェックエラー`
<img width="450" alt="キャプチャ3.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403060/e2e0ec9e-2cef-59b8-deb9-9e23264ef2fc.png">

実際に出力されるエラーメッセージは各属性の`()`内にある`ErrorMessage`で指定している。
ちなみに、`[StringLength(10, ErrorMessage = "{0}は{1}文字以内で入力してください。")]`のように`{n}`となっている個所にはそれぞれ、

- `{0}`：`[Display(Name = "")]`で指定したプロパティの表示名称
- `{1~n}`：各属性の第一引数～第n引数

が表示される。

# エラーメッセージの表示位置について
前節で`@Html.ValidationMessageFor()`に各プロパティの属性チェックのエラーメッセージがレンダリングされると書いたが、実際のViewファイルへの記述は以下のようになっている。

``` html:Index.cshtml
@model HtmlHelperTest.ViewModels.HelperViewModel

@using (@Html.BeginForm())
{
    @Html.AntiForgeryToken()
    <div>
        <div>
            @Html.LabelFor(model => model.SumpleProperty)
        </div>
        <div>
            @Html.TextBoxFor(model => model.SumpleProperty)
            @Html.ValidationMessageFor(model => model.SumpleProperty)
        </div>
        <div>
            <input type="submit" value="OK" />
        </div>
    </div>
}
```

他のHtmlHelper同様、`@Html.ValidationMessageFor()`の引数にプロパティを指定するだけだ。
このような記述によって、
<img width="450" alt="キャプチャ3.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403060/b1393304-1862-becb-f05d-8bd0cd1258ad.png">

このように任意の場所にエラーメッセージを表示させることができる。

しかし、場合によっては画面上部や特定の場所にエラーメッセージをまとめて表示させたい場合もある。
そのような場合には、以下のように`@Html.ValidationSummary()`を追加して、第一引数に`false`を指定すればよい。

``` html:Index.cshtml
@model HtmlHelperTest.ViewModels.HelperViewModel

@using (@Html.BeginForm())
{
    @Html.AntiForgeryToken()
    <!-- ↓変更箇所　コード追加↓ -->
    @Html.ValidationSummary(false)
    <!-- ↑変更箇所　コード追加↑ -->
    <div>
        <div>
            @Html.LabelFor(model => model.SumpleProperty)
        </div>
        <div>
            @Html.TextBoxFor(model => model.SumpleProperty)
            <!-- ↓変更箇所　エラー項目であることだけ分かるように"*"のみ表示↓ -->
            @Html.ValidationMessageFor(model => model.SumpleProperty, "*")
            <!-- ↑変更箇所　エラー項目であることだけ分かるように"*"のみ表示↑ -->
        </div>
        <div>
            <input type="submit" value="OK" />
        </div>
    </div>
}
```

こうすることで、以下のように箇条書きでエラー一覧が表示されるようになる。
また、ここではエラー項目が分かるように"*"をエラーメッセージの代わりに表示させている。
<img width="453" alt="キャプチャ4.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403060/5bcbbd7b-6666-4783-b4cd-308bb9daeb70.png">
ちなみに、`@Html.ValidationSummary()`の第二引数に箇条書きの上に表示させるメッセージを指定できる。
※ここでは`@Html.ValidationSummary(false, "エラー一覧")`として実行。

# Htmlタグに自動付与されるValidation関連クラス
ここまでで属性チェックによるエラーメッセージを任意の箇所に表示させることができたが、今のままだと以下のようにPOST前の状態でもエラーメッセージが表示されっぱなしになる。
<img width="456" alt="キャプチャ5.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403060/ec901fae-c807-331a-4708-99585bfdb8bd.png">

GET時やエラーがない場合は非表示にしたい。
　⇒**`@Html.ValidationSummary()`や`Html.ValidationMessageFor()`が
　　自動で付与してくれるクラスを利用する。**

`@Html.ValidationSummary()`および`Html.ValidationMessageFor()`は、プロパティのエラーの有無によって自動的にHtmlタグへクラスを付与してくれる。
付与されるクラスの一覧は以下。

- validation-summary-valid：エラーがない場合、`@Html.ValidationSummary()`の生成したdiv要素に付与。
- validation-summary-errors：エラーがある場合、`@Html.ValidationSummary()`の生成したdiv要素に付与。
- input-validation-error：エラーがある場合、エラーとなったプロパティを使用している要素に付与。
- field-validation-valid：エラーがない場合、`Html.ValidationMessageFor()`の生成したspan要素に付与。
- field-validation-error：エラーがある場合、`Html.ValidationMessageFor()`の生成したspsn要素に付与

つまり、class="validation-summary-valid"の要素は非表示とし、class="validation-summary-errors"の要素は表示させればよいことになる。
これらは基本的にSite.css内に記載する。

[参考サイト](https://www.atmarkit.co.jp/fdotnet/aspnetmvc3/aspnetmvc3_04/aspnetmvc3_04_03.html)によると以前はSite.cssに以下のような記述がデフォルトで用意されていたようだが、現在は自分で記載する必用があるようだ。
（もしかして今は別の方法がスタンダードになっている？）

```css:Site.css
.field-validation-error
{
  color: #ff0000;
}

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

.input-validation-error
{
  border: 1px solid #ff0000;
  background-color: #ffeeee;
}

.validation-summary-errors
{
  font-weight: bold;
  color: #ff0000;
}

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

この状態でビルドすると、当然以下のように必用な時のみ表示させることができる。
（ついでにエラーメッセージが赤字になっている）

`GET時`
<img width="457" alt="キャプチャ6.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403060/8c4490bd-53d4-eef8-9c00-abfd8a303224.png">


`POST時　桁数チェックエラー`
<img width="458" alt="キャプチャ7.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403060/4a527487-7572-1534-f5c9-7193b375e960.png">


---
参考サイト：[@IT 連載：ASP.NET MVC入門](https://www.atmarkit.co.jp/fdotnet/aspnetmvc3/aspnetmvc3_04/aspnetmvc3_04_03.html)