就職・転職書類の作成でお困りの方へ
Tips
PR

【C#】Minimal API でのバリデーション実装方法

かなむ
記事内に商品プロモーションを含む場合があります

C#のMinimal APIでは、モデルのバリデーションを簡単に実装できます。本記事では、Minimal APIでバリデーションを行う 3 つの方法を紹介します。

  • データアノテーションを使う
    必須項目、文字数制限などのシンプルなバリデーションを実装できる
  • カスタムバリデーションを使う
    if文などを使った複雑なバリデーションを実装できる
  • FluentValidation を利用する
    より柔軟で拡張性のあるバリデーションを実装できる
かなむ
かなむ

それぞれの方法について、コード例を交えながら解説していきます。

なお、本記事で使用するコードは下記の記事で作成したものです。

【C#】VSCodeでC#の最小WEB API(Minimal API)を作成する方法と仕組み
【C#】VSCodeでC#の最小WEB API(Minimal API)を作成する方法と仕組み

データアノテーションを使う

もっともシンプルでよく使われる方法です。属性(アノテーション)をモデルのプロパティに直接定義できます。使用するには、C# の System.ComponentModel.DataAnnotations を利用する必要があります。

下記では、Model.csに定義したUserクラスに、System.ComponentModel.DataAnnotationsの注入と、[]を用いたバリデーションルールを適用しています。

using System.ComponentModel.DataAnnotations;

namespace Model
{
    public class User
    {
        [Required(ErrorMessage = "名前は必須です")]
        [StringLength(50, MinimumLength = 3, ErrorMessage = "名前は3文字以上50文字以下である必要があります")]
        public string Name { get; set; } = string.Empty;

        [Range(18, 100, ErrorMessage = "年齢は18歳以上100歳以下である必要があります")]
        public int Age { get; set; }
    }
}
  • Required
    必須チェック
  • StringLength({最大文字数},MinimumLength = {最小文字数})
    文字数チェック。オプションとして最小文字数も指定できる
  • Range({最小値},{最大値})
    数値の範囲チェック

各属性にはErrorMessageを設定できます。それぞれのチェックでエラーになったとき、どういったエラーメッセージを出力するかを決められます。

program.csでバリデーションが有効になるよう記載します。こちらでもSystem.ComponentModel.DataAnnotationsを追加します。

using System.ComponentModel.DataAnnotations;
using Model;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapPost("/users", (User user) =>
{
    var validationResults = new List<ValidationResult>();
    var context = new ValidationContext(user, null, null);

    if (!Validator.TryValidateObject(user, context, validationResults, validateAllProperties: true))
    {
        return Results.BadRequest(validationResults.Select(v => v.ErrorMessage));
    }

    return Results.Ok($"Received user: {user.Name}, Age: {user.Age}");
});

app.Run();
program.cs解説
  • var validationResults = new List();
    バリデーションの結果(エラーメッセージなど)を格納するための空のリストを用意
  • var context = new ValidationContext(user, null, null);
    「どのオブジェクトを検証するか」という情報を持つ ValidationContext を作成
    ここでは user オブジェクト(POSTされたユーザー情報)を検証対象にしています。
  • if (!Validator.TryValidateObject(user, context, validationResults, validateAllProperties: true))
    Validator.TryValidateObject で、user オブジェクトのすべてのプロパティに対して、アノテーションのルールを使ってチェック
    false が返ってきたら、何かしらのエラーあり
     第1引数:検証するオブジェクト(user) 
     第2引数:検証用のコンテキスト(context)
     第3引数:エラー結果を格納するリスト(validationResults)
     第4引数:すべてのプロパティを検証するか(true で検証する)

データアノテーションを使ったバリデーションは、ちょっとしたチェックに向いています。

実行してみるとこうなります。

パラメータ

処理結果

カスタムバリデーション

データアノテーションでは対応できない、少し複雑な条件(例えば「名前に数字が含まれていないか」など)を実現したいときは、IValidatableObject を使ってモデルの中でバリデーションロジックを書く方法があります。

using System.ComponentModel.DataAnnotations;

namespace Model
{
    public class User : IValidatableObject
    {
        public string Name { get; set; } = string.Empty;
        public int Age { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if (Name.Any(char.IsDigit))
            {
                yield return new ValidationResult("名前に数字を含めることはできません", new[] { nameof(Name) });
            }
        }
    }
}

上記では、名前により詳細な条件を付与しています。カスタムバリデーションをする際のprogram.csはデータバリデーションを実施する場合と同じです。

実施してみるとこんな感じです。

パラメータ

処理結果

もちろん、データアノテーションと組み合わせることもできます。その場合、データアノテーションによるバリデーション→カスタムバリデーションの順で検査されます。

using System.ComponentModel.DataAnnotations;

namespace Model
{
    public class User: IValidatableObject
    {
        [Required(ErrorMessage = "名前は必須です")]
        [StringLength(50, MinimumLength = 3, ErrorMessage = "名前は3文字以上50文字以下である必要があります")]
        public string Name { get; set; } = string.Empty;

        [Range(18, 100, ErrorMessage = "年齢は18歳以上100歳以下である必要があります")]
        public int Age { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if (Name.Any(char.IsDigit))
            {
                yield return new ValidationResult("名前に数字を含めることはできません", new[] { nameof(Name) });
            }
        }
    }
}

パラメータ

処理結果

カスタムバリデーションは柔軟ですが、ロジックがモデル内に埋め込まれるため、コードが少し見づらくなります。

FluentValidation

もっと柔軟で拡張しやすいバリデーションが必要な場合は、FluentValidation というライブラリを使う方法があります。ルールを外部のクラスで定義できるので、コードが整理しやすくなります。

FluentValidationを使用するには、まずパッケージを追加します。

dotnet add package FluentValidation.AspNetCore

モデルとバリデータを定義します。

using FluentValidation;

namespace Model
{
    public class User
    {
        public string Name { get; set; } = string.Empty;
        public int Age { get; set; }
    }

    public class UserValidator : AbstractValidator<User>
    {
        public UserValidator()
        {
            RuleFor(x => x.Name)
                .NotEmpty().WithMessage("名前は必須です")
                .Length(3, 50).WithMessage("名前は3文字以上50文字以下である必要があります");

            RuleFor(x => x.Age)
                .InclusiveBetween(18, 100).WithMessage("年齢は18歳以上100歳以下である必要があります");
        }
    }
}

program.csを下記のように書き換えます。

using FluentValidation;
using FluentValidation.AspNetCore;
using Model;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddValidatorsFromAssemblyContaining<UserValidator>();

var app = builder.Build();

app.MapPost("/users", (User user, IValidator<User> validator) =>
{
    var validationResult = validator.Validate(user);

    if (!validationResult.IsValid)
    {
        return Results.BadRequest(validationResult.Errors.Select(e => e.ErrorMessage));
    }

    return Results.Ok(new { Message = "ユーザー登録成功", User = user });
});

app.Run();

FluentValidationについては別の記事で詳細を解説しているので、参考になれば幸いです。

FluentValidationについて
【C#】Minimal API での FluentValidation 活用方法
【C#】Minimal API での FluentValidation 活用方法

まとめ

Minimal APIにおけるバリデーションの実装についてまとめました。

方法向いているケース特徴
データアノテーション簡単なチェックだけで十分なときシンプルで手軽に使える
カスタムバリデーションちょっと複雑なルールが必要なとき自由度は高いが、見通しはやや悪い
FluentValidation柔軟で拡張しやすい構成にしたいとき別クラスで定義でき、保守性も高い

開発するシステムの規模やチームのコーディング方針によって、最適なバリデーション方法は異なります。最初はシンプルに始めて、必要に応じて FluentValidation に移行するのも良いでしょう。

かなむ
かなむ

参考になればうれしいです。

ABOUT ME
かなむ
かなむ
現役SE。javaとc#とpythonとvbの経験あり。アプリ開発メイン。インフラ、特にネットワークは苦手。2児の父。好きな寿司ネタは「鯛」。将来は離島に別荘を構えたい。

記事URLをコピーしました