カスタム ValidationAttribute と Blazor 検証

· 2分で読める

すべてをカスタマイズ

おそらく私のすべての投稿でご覧になっていると思いますが、カスタム属性、カスタム例外処理、サービス コレクション インジェクションなどに関する投稿をすでに書いているため、私はすべてをできるだけクリーンに保つよう努めています。

時間が経つにつれて、この種のコーディング方法により、私と私のチームが残業を改善し、問題を見つけやすくなり、コードを可能な限り分離する方法が得られることに気づきました。

はい、この素晴らしい話の後で、私は最近いつものように Blazor に取り組んでおり、何年も開発を続けた結果、カスタム検証属性を作成できることがわかりました。

うん、面白いね、何年も経ったのに…

カスタム検証属性

このアイデアは実際に仕事から生まれました。私たちは常にすべての場所で検証を行っていますが、同じ検証プロセスを必要とするフィールドがいくつかあったので、そこに何かがあるかもしれないと考えました…カスタム検証属性のようなものです。

そこで Microsoft のドキュメントを調べたところ、次のようにカスタム検証属性を作成してプロパティに割り当てることができることがわかりました。

public class StringLengthRangeAttribute : ValidationAttribute
{
    public int Minimum { get; set; }
    public int Maximum { get; set; }

    public StringLengthRangeAttribute()
    {
        this.Minimum = 0;
        this.Maximum = int.MaxValue;
    }

    public override bool IsValid(object value)
    {
        string strValue = value as string;
        if (!string.IsNullOrEmpty(strValue))
        {
            int len = strValue.Length;
            return len >= this.Minimum && len <= this.Maximum;
        }
        return true;
    }
}

次のような単純なクラスで使用します。

[Required]
[StringLengthRange(Minimum = 10, ErrorMessage = "Must be >10 characters.")]

[StringLengthRange(Maximum = 20)]

[Required]
[StringLengthRange(Minimum = 10, Maximum = 20)]

カスタムバリデーター

そこで、特定のビジネスケースに必要なバリデーターを用意しました。これは、最初の 20 文字をカウントし、9 つの数字とハイフンを省略し、通常は国コードとなる 2 文字で終わります。つまり、次のようなものになります。 123456789-123456789-ES

私は最終的にこのようなものを思いつきました、それは本当に単純ですが、うまくいきます:

using System;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;

public class SpecialStringValidatorAttribute : ValidationAttribute
{
    private const int TotalLength = 22;
    private const string Pattern = @"^(\d{10})-(\d{10})-([A-Za-z]{2})$";

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        string strValue = value as string;

        if (!string.IsNullOrEmpty(strValue))
        {
            if (strValue.Length != TotalLength)
            {
                return new ValidationResult($"The string must be {TotalLength} characters long.");
            }

            if (!Regex.IsMatch(strValue, Pattern))
            {
                return new ValidationResult("The string must follow the pattern: 1234567890-1234567890-AB");
            }

            return ValidationResult.Success;
        }

        return new ValidationResult("The string cannot be null or empty.");
    }
}

テスト

念のため、彼らのためにいくつかのテストも書きました。

public class SpecialStringValidatorTests
{
    [Theory]
    [InlineData("1234567890-1234567890-AB", true)]
    [InlineData("1234567890-1234567890-XY", true)]
    [InlineData("1234567890-1234567890-A1", false)]
    [InlineData("1234567890-1234567890-A", false)]
    [InlineData("1234567890-123456789-AB", false)]
    [InlineData("1234567890-1234567890", false)]
    [InlineData("1234567890-1234567890-ABCDE", false)]
    public void SpecialStringValidatorTest(string input, bool expectedResult)
    {
        // Arrange
        var validator = new SpecialStringValidatorAttribute();

        // Act
        var result = validator.IsValid(input);

        // Assert
        Assert.Equal(expectedResult, result);
    }
}

そして実行すると、次の結果が得られました。

Microsoft (R) Test Execution Command Line Tool Version 16.9.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...

A total of 1 test files matched the specified pattern.

Test run in progress...

Passed!  - SpecialStringValidatorTests.SpecialStringValidatorTest(input: "1234567890-1234567890-AB", expectedResult: True)
Passed!  - SpecialStringValidatorTests.SpecialStringValidatorTest(input: "1234567890-1234567890-XY", expectedResult: True)
Passed!  - SpecialStringValidatorTests.SpecialStringValidatorTest(input: "1234567890-1234567890-A1", expectedResult: False)
Passed!  - SpecialStringValidatorTests.SpecialStringValidatorTest(input: "1234567890-1234567890-A", expectedResult: False)
Passed!  - SpecialStringValidatorTests.SpecialStringValidatorTest(input: "1234567890-123456789-AB", expectedResult: False)
Passed!  - SpecialStringValidatorTests.SpecialStringValidatorTest(input: "1234567890-1234567890", expectedResult: False)
Passed!  - SpecialStringValidatorTests.SpecialStringValidatorTest(input: "1234567890-1234567890-ABCDE", expectedResult: False)

Test Run Successful.
Total tests: 7
     Passed: 7
 Total time: 1.7296 Seconds

カスタム検証と Blazor

この関数が使用できることがわかったので、それを Blazor に実装するのは明らかに良い考えですよね?

前に示したモデル Person を使用するこのフォームがあると仮定しましょう。

@using Models
@page "/"

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

<EditForm Model=@Person FormName="PersonForm">
  <DataAnnotationsValidator/>
  <ValidationSummary/>
  <div class="form-group">
    <label for="Name">Name</label>
    <InputText @bind-Value=Person.Name class="form-control" id="Name" />
    <ValidationMessage For="() => Person.Name"/>
  </div>
  <div class="form-group">
    <label for="MySpecialString">My special string</label>
    <InputText @bind-Value=Person.MySpecialString class="form-control" id="Name" />
    <ValidationMessage For="() => Person.MySpecialString"/>
  </div>
  <div class="form-group">
    <label for="Age">Age</label>
    <InputNumber @bind-Value=Person.Age class="form-control" id="Age" />
    <ValidationMessage For=@(() => Person.Age) />
  </div>
  <input type="submit" class="btn btn-primary" value="Save"/>
</EditForm>

@code {
  Person Person = new Person();
}

これを実行するとすぐに、次のエラーが表示されます。

そして、必要なものをそのまま入力すると、次のことがわかります。

正直に言うと、少なくともこれらのフォームを検証するロジックをカスタム検証属性に移動する必要があることは明らかです。これにより、そのログインのコードを 1 か所に自由に保存できるようになり、後で API や別のアプリケーションで使用できるようになります。

気に入っていただければ幸いです。ご質問がある場合、または連絡したい場合は、ためらわずにご連絡ください。