Razor Pagesのフォームの入力内容を設定したオブジェクトを検証する - ASP.NET Core

Razor Pagesのフォームの入力内容を設定したオブジェクトを検証するコードを紹介します。

概要

こちらの記事ではRazor Pagesのフォームの検証を実装しました。
この記事では、入力された内容をオブジェクトに設定した場合で検証するプログラムのコードを紹介します。

実装方針

オブジェクトに複数の値が代入されるため、複数の検証クラスをプロパティに設定します。 また、検証結果のValidationResult作成時にどのフィールドのエラーかを示す、memberNamesフィールドを設定します。

プログラム例

ASP.NET Core アプリケーションを作成し、以下のファイル、コードを作成します。

コード

Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();

app.UseStaticFiles();
app.UseRouting();
app.MapRazorPages();
app.Run();
Pages/CustomValidationObject01.cshtml
@page
@model RazorPagesValidationObject.Pages.CustomValidationObject01Model
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
}
<html>
<head>
</head>
<body>
  <h2>入力内容のカスタム検証のデモ (オブジェクト検証)</h2>
  <form method="post">
    <input type="text" asp-for="inputData.input1" /><br />
    <text>@Model.ErrorMessageText1</text><br />
    <input type="text" asp-for="inputData.input2" /><br />
    <text>@Model.ErrorMessageText2</text><br />
    <input type="text" asp-for="inputData.input3" /><br />
    <text>@Model.ErrorMessageText3</text><br />
    <input type="submit" value="Exec" />
  </form>

  <p>@Model.MessageText</p>
</body>
</html>
Pages/CustomValidationObject01.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace RazorPagesValidationObject.Pages
{
  public class MyInputData
  {
    public string input1 { get; set; } = "";
    public string input2 { get; set; } = "";
    public string input3 { get; set; } = "";
  }

  public class MyValue1Attribute : ValidationAttribute
  {
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
      MyInputData data = (MyInputData)value;

      if (data.input1 == "Penguin" || data.input1 == "Duck") {
        return ValidationResult.Success;
      }
      else {
        return new ValidationResult("Penguin か Duck のどちらかを入力してください。", new List<string>(){ "inputText1" });
      }
    }
  }

  public class MyValue2Attribute : ValidationAttribute
  {
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
      MyInputData data = (MyInputData)value;

      int ivalue;
      if (int.TryParse((string)data.input2, out ivalue) == true) {
        return ValidationResult.Success;
      }
      else {
        return new ValidationResult("数値を入力してください。", new List<string>() { "inputText2" });
      }
    }
  }

  public class MyValue3Attribute : ValidationAttribute
  {
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
      MyInputData data = (MyInputData)value;

      System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("^[A-Z]$");
      System.Text.RegularExpressions.Match match = reg.Match(data.input3);

      if (match.Success == true) {
        return ValidationResult.Success;
      }
      else {
        return new ValidationResult("AからZの大文字一文字を入力してください。", new List<string>() { "inputText3" });
      }
    }
  }

  public class MyValueMultiAttribute : ValidationAttribute
  {
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
      MyInputData data = (MyInputData)value;
      int ivalue;
      if (data.input1 == "Penguin") {
        if (int.TryParse((string)data.input2, out ivalue) == true) {
          if (10 < ivalue) {
            return new ValidationResult("Penguinを入力した場合は、10以下の値を設定してください。", new List<string>() { "inputText1","inputText2" });
          }
        }
      }
      return ValidationResult.Success;
    }
  }


  public class CustomValidationObject01Model : PageModel
  {
    [BindProperty]
    [MyValue1Attribute]
    [MyValue2Attribute]
    [MyValue3Attribute]
    [MyValueMultiAttribute]
    public MyInputData inputData { get; set; }

    public string ErrorMessageText1 { get; set; } = "";
    public string ErrorMessageText2 { get; set; } = "";
    public string ErrorMessageText3 { get; set; } = "";
    public string MessageText { get; set; } = "";


    public void OnGet()
    {
    }

    public PageResult OnPost()
    {
      if (ModelState.IsValid == true) {
        MessageText = "処理は成功しました。";
      }
      else {


        ErrorMessageText1 = "";
        if (ViewData.ModelState["inputData.inputText1"] != null) {
          ModelStateEntry mse = ViewData.ModelState["inputData.inputText1"]!;
          foreach (ModelError me in mse.Errors) {
            ErrorMessageText1 += me.ErrorMessage;
          }
        }

        ErrorMessageText2 = "";
        if (ViewData.ModelState["inputData.inputText2"] != null) {
          ModelStateEntry mse = ViewData.ModelState["inputData.inputText2"]!;
          foreach (ModelError me in mse.Errors) {
            ErrorMessageText2 += me.ErrorMessage;
          }
        }

        ErrorMessageText3 = "";
        if (ViewData.ModelState["inputData.inputText3"] != null) {
          ModelStateEntry mse = ViewData.ModelState["inputData.inputText3"]!;
          foreach (ModelError me in mse.Errors) {
            ErrorMessageText3 += me.ErrorMessage;
          }
        }

      }

      return Page();
    }

  }
}

解説

入力値を保持するクラスの作成

入力値を保持するクラスを作成します。
MyInputDataの名称とし、プロパティを3つ宣言します。
  public class MyInputData
  {
    public string input1 { get; set; } = "";
    public string input2 { get; set; } = "";
    public string input3 { get; set; } = "";
  }

検証クラス

ValidationAttributeから派生した検証クラスを4つ作成します。

MyInputData.input1プロパティ(一番上のテキストボックス)用の検証クラスです。
"Penguin"か"Duck"のどちらかの値が入力されていないとエラーになります。
エラー時の memberNames の値は一番上のテキストボックスを表す "inputText1" を設定します。
  public class MyValue1Attribute : ValidationAttribute
  {
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
      MyInputData data = (MyInputData)value;

      if (data.input1 == "Penguin" || data.input1 == "Duck") {
        return ValidationResult.Success;
      }
      else {
        return new ValidationResult("Penguin か Duck のどちらかを入力してください。", new List<string>(){ "inputText1" });
      }
    }
  }

MyInputData.input2プロパティ(2番目のテキストボックス)用の検証クラスです。
数値が入力されていないとエラーになります。
エラー時の memberNames の値は2番目のテキストボックスを表す "inputText2" を設定します。
  public class MyValue2Attribute : ValidationAttribute
  {
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
      MyInputData data = (MyInputData)value;

      int ivalue;
      if (int.TryParse((string)data.input2, out ivalue) == true) {
        return ValidationResult.Success;
      }
      else {
        return new ValidationResult("数値を入力してください。", new List<string>() { "inputText2" });
      }
    }
  }

MyInputData.input3プロパティ(3番目のテキストボックス)用の検証クラスです。
AからZの大文字一文字が入力されていないとエラーになります。
エラー時の memberNames の値は3番目のテキストボックスを表す "inputText3" を設定します。
  public class MyValue3Attribute : ValidationAttribute
  {
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
      MyInputData data = (MyInputData)value;

      System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("^[A-Z]$");
      System.Text.RegularExpressions.Match match = reg.Match(data.input3);

      if (match.Success == true) {
        return ValidationResult.Success;
      }
      else {
        return new ValidationResult("AからZの大文字一文字を入力してください。", new List<string>() { "inputText3" });
      }
    }
  }

複数のフィールドを利用した検証ロジックです。 一番上のテキストボックスに"Penguin"を入力した場合は、2番目のテキストボックスに10以下の値が入力されていないとエラーになります。
エラー時の memberNames の値は1つ目と2つ目の両方のテキストボックスの入力値に関係しているため、 1,2番目のテキストボックスを表す "inputText1"と"inputText2" を設定します。
  public class MyValueMultiAttribute : ValidationAttribute
  {
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
      MyInputData data = (MyInputData)value;
      int ivalue;
      if (data.input1 == "Penguin") {
        if (int.TryParse((string)data.input2, out ivalue) == true) {
          if (10 < ivalue) {
            return new ValidationResult("Penguinを入力した場合は、10以下の値を設定してください。", new List<string>() { "inputText1","inputText2" });
          }
        }
      }
      return ValidationResult.Success;
    }
  }

ページモデルクラス プロパティ

ページモデルクラスに、MyInputDataクラスの inputData プロパティを宣言します。 プロパティの検証のため、先に作成した検証クラス4つをプロパティ属性で指定します。
    [BindProperty]
    [MyValue1Attribute]
    [MyValue2Attribute]
    [MyValue3Attribute]
    [MyValueMultiAttribute]
    public MyInputData inputData { get; set; }

RazorPages フォーム

RazorPagesのコードです。formタグ内のinputタグで入力値を格納する asp-for 属性に ページモデルクラスのinputDataプロパティを指定します。 一番上のテキストボックスの代入先が inputData.input1 2つ目のテキストボックスの代入先が inputData.input2 3つ目が inputData.input3 とします。
  <form method="post">
    <input type="text" asp-for="inputData.input1" /><br />
    <text>@Model.ErrorMessageText1</text><br />
    <input type="text" asp-for="inputData.input2" /><br />
    <text>@Model.ErrorMessageText2</text><br />
    <input type="text" asp-for="inputData.input3" /><br />
    <text>@Model.ErrorMessageText3</text><br />
    <input type="submit" value="Exec" />
  </form>

ページモデルクラスPOST処理

ページモデルクラスのPOST処理部分です。はじめに、IsValid プロパティの値を確認して、検証結果を確認します。 IsValid プロパティの値がtrueであれば、入力値に問題が無いため、"処理は成功しました。"のメッセージを画面に表示します。
IsValidの値がfalseの場合は、入力値にエラーがあるため、エラーの内容を表示します。検証の結果は、ModelState オブジェクトに保持され、 キーの値が (プロパティ名).(ValidationResultオブジェクト作成時に指定したfieldName) となります。

一番上のテキストボックスのエラーメッセージは、ViewData.ModelState["inputData.inputText1"] で取得した、ModelStateEntry オブジェクトをループして、 ModelError オブジェクトのErrorMessage の値を取得して画面に表示します。
同様に2番目のテキストボックスのエラーメッセージはViewData.ModelState["inputData.inputText2"] に 3番めのテキストボックスのエラーメッセージは ViewData.ModelState["inputData.inputText3"]に設定されています。
public PageResult OnPost()
{
  if (ModelState.IsValid == true) {
    MessageText = "処理は成功しました。";
  }
  else {
    ErrorMessageText1 = "";
    if (ViewData.ModelState["inputData.inputText1"] != null) {
      ModelStateEntry mse = ViewData.ModelState["inputData.inputText1"]!;
      foreach (ModelError me in mse.Errors) {
        ErrorMessageText1 += me.ErrorMessage;
      }
    }

    ErrorMessageText2 = "";
    if (ViewData.ModelState["inputData.inputText2"] != null) {
      ModelStateEntry mse = ViewData.ModelState["inputData.inputText2"]!;
      foreach (ModelError me in mse.Errors) {
        ErrorMessageText2 += me.ErrorMessage;
      }
    }

    ErrorMessageText3 = "";
    if (ViewData.ModelState["inputData.inputText3"] != null) {
      ModelStateEntry mse = ViewData.ModelState["inputData.inputText3"]!;
      foreach (ModelError me in mse.Errors) {
        ErrorMessageText3 += me.ErrorMessage;
      }
    }
  }

  return Page();
}

実行結果

プロジェクトを実行し、RazorPagesのURLにアクセスします。下図のページが表示されます。


正常な値を入力します。テキストボックスに順に "Duck" "20" "F" を入力します。入力後[Exec]ボタンをクリックします。


ボタンをクリックすると、正しい入力値のため、「処理は成功しました。」のメッセージが表示されます。


続いて、テキストボックスに順に "A" "B" "5" を入力します。入力後[Exec]ボタンをクリックします。


検証で失敗し、エラーメッセージがそれぞれのテキストボックスの下部に表示されます。


複数のフィールドの検証も試します。一番上のテキストボックスに"Penguin"を入力し、2番目のテキストに"20"を入力し、[Exec]ボタンをクリックします。


Penguinを入力した場合は、10より大きい値を入力するとエラーになる旨のメッセージが表示されます。


2つ目のテキストボックスの値を10以下の値に修正し、[Exec]ボタンをクリックすると検証が通り、「処理は成功しました。」のメッセージに変わります。


RazorPagesのフォームの値をオブジェクトに代入した場合の検証処理を実装できました。

著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2023-03-15
作成日: 2023-03-15
iPentec all rights reserverd.