プログラムを中心とした個人的なメモ用のブログです。 タイトルは迷走中。
内容の保証はできませんのであしからずご了承ください。

2017/11/07

ASP.NET Core におけるバリデーションの基本

update2017/11/14 event_note2017/11/07 3:07

ASP.NET Core ではバリデーションをどうやってやるのかなと思い調べてみました。
基本的には属性を追加するだけで簡単にできます。

環境

  • Visual Studio 2017
  • ASP.NET Core 2.0

サンプル

View です。 ここでは以下のような入力フォームがあったとします。

@model Sample.Models.Account

<!-- 中略 -->

<div id="login-form" class="container well">
    <form asp-controller="Account" asp-action="Login">
        <div class="form-group">
            <label asp-for="Username" class="control-label">Username</label>
            <input asp-for="Username" type="text" class="form-control input-lg">
            <span asp-validation-for="Username" class="text-danger"></span>
        </div>
        <div class="form-group">
            <label asp-for="Password" class="control-label">Password</label>
            <input asp-for="Password" type="password" class="form-control input-lg">
            <span asp-validation-for="Password" class="text-danger"></span>
        </div>
        <div asp-validation-summary="ModelOnly" class="text-danger"></div>
        <button type="submit" class="btn btn-success btn-block btn-lg">Login</button>
    </form>
</div>

データクラスは以下です。

public class Account
{
    public string Username { get; set; }
    public string Password { get; set; }
}

簡単な解説

まず、以下のコードで、この View で扱う model を定義しています。

@model SD2APCT1.Models.Account

ここではアカウント情報を扱うためのクラス Account を model として定義しています。

入力値は asp-for="プロパティ名" とすることで、model の該当のプロパティにバインディングされます。 尚、大文字小文字が異なっていてもきちんとバインディングされるようです。

asp-validation-for="プロパティ名" は、指定したプロパティのバリデーションでエラーとなったときにメッセージを表示する箇所に指定します。 このエラーは後述するように、model とするクラスのフィールドの属性に従ってクライアントサイドでチェックされるようです。 尚、エラー以外の場合では HTML タグ自体出力されません。

asp-validation-summary を指定した箇所には、Controller で内容をチェックし、エラーが発生したときのメッセージが表示されます。 asp-validation-summary の属性値には All ModelOnly None が指定できるようです。

詳細は公式のドキュメントを参照してください。
また、メッセージは li タグで出力されるようです。

入力を必須とする

フィールドに [Required] を付加します。

using System.ComponentModel.DataAnnotations;

public class Account
{
    [Required]
    public string Username { get; set; }
    [Required]
    public string Password { get; set; }
}

他に指定できる属性は以下を参照してください。

これに対し、アクションメソッドで以下のように記述します。

[HttpPost]
[AutoValidateAntiforgeryToken]
public async Task<IActionResult> Login(Account user)
{
    if (ModelState.IsValid)
    {
        const string badUserNameOrPasswordMessage = "Username or password is incorrect.";
        if (user == null)
        {
            ModelState.AddModelError(string.Empty, badUserNameOrPasswordMessage);
            return View(user);
        }

        // 認証
        var lookupUser = MaintenanceAccount.IsAuthentication(user);
        if (lookupUser == null)
        {
            ModelState.AddModelError(string.Empty, badUserNameOrPasswordMessage);
            return View(user);
        }

        // Cookies 認証スキームで新しい ClaimsIdentity を作成し、ユーザー名を追加します。
        var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
        identity.AddClaim(new Claim(ClaimTypes.Name, lookupUser.Username));

        // クッキー認証スキームと、上の数行で作成されたIDから作成された新しい ClaimsPrincipal を渡します。
        await HttpContext.SignInAsync(new ClaimsPrincipal(identity));

        return RedirectToAction(nameof(HomeController.Index), "Home");
    }
    return View(user);
}

ModelState.IsValid により、前述の [Required] 属性などがチェックされて有効な値かどうかが判定できます。

この状態で未入力のままボタンをクリックすると、以下のようにエラーが表示されます。

表示するメッセージを変更したい場合は以下のように記述します。

using System.ComponentModel.DataAnnotations;

public class Account
{
    [Required(ErrorMessage = "ユーザー名は必須入力です。")]
    public string Username { get; set; }
    [Required(ErrorMessage = "パスワードは必須入力です。")]
    public string Password { get; set; }
}

また、多言語対応などにより、リソースファイルからテキストを取得したい場合は以下のように記述します。

using System.ComponentModel.DataAnnotations;

public class Account
{
    [Required(ErrorMessageResourceType = typeof(Resources.Messages),
        ErrorMessageResourceName = "ユーザー名は必須入力です。")]
    public string Username { get; set; }
    [Required(ErrorMessageResourceType = typeof(Resources.Messages),
        ErrorMessageResourceName = "パスワードは必須入力です。")]
    public string Password { get; set; }
}

入力値に対するチェック

Controller で入力値のチェックを行いエラーとする場合は、以下のように記述します。
ここでは例のため、無条件でエラーとしています。

[HttpPost]
[AutoValidateAntiforgeryToken]
public async Task<IActionResult> Login(Account user)
{
    ModelState.AddModelError(string.Empty, "ユーザー名またはパスワードが不正です。");
    return View(user);
}

参考 URL