こんにちは、MLBお兄さんこと松村です。
.NET 10 のプレビューリリースが始まり、先日 Preview 3 が公開されました。そのなかで気になったアップデートについて検証してみました。
ASP.NET Core でのモデルバリデーション
ASP.NET Core では DataAnnotations
属性を使用して、モデルバリデーションを行うことが一般的です。
モデルバリデーションとは、HTTPリクエストで送信されたデータに対して、定義された規則を満たしているかをチェックする処理のことです。
規則のチェックとは、例えば以下のような場合です。
- 必須項目が入力されているか
- 年齢欄は数字のみ入力されているか
- メールアドレス欄にはメールアドレス形式の値が指定されているか
- 有効期間欄の日付が逆転していないか
このようなチェックは、ASP.NET Core ではこのようなモデルを定義し、フレームワークの機能でバリデーションを構成します。
MVC や Razor Pages などは標準機能としてモデルバリデーションを行うことができます。
using System.ComponentModel.DataAnnotations; namespace WebApplication1.Models; public class User : IValidatableObject { [Key] public int Id { get; set; } [Display(Name = "名前")] [Required(ErrorMessage = "{0}が指定されていません。")] public string Name { get; set; } [Display(Name = "年齢")] [Required(ErrorMessage = "{0}が指定されていません。")] [RegularExpression(@"^[0-9]+$", ErrorMessage = "{0}は数字のみ入力してください。")] public int Age { get; set; } [Display(Name = "メールアドレス")] [Required(ErrorMessage = "{0}が指定されていません。")] [EmailAddress(ErrorMessage = "{0}の形式が不正です。")] public string Email { get; set; } [Display(Name = "有効期限(開始)")] [Required(ErrorMessage = "{0}が指定されていません。")] [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:yyyy/MM/dd}", ApplyFormatInEditMode = true)] public DateOnly TermFrom { get; set; } [Display(Name = "有効期限(終了)")] [Required(ErrorMessage = "{0}が指定されていません。")] [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:yyyy/MM/dd}", ApplyFormatInEditMode = true)] public DateOnly TermTo { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { if (TermFrom > TermTo) { yield return new ValidationResult( "日付が逆転しています。", new[] { nameof(TermFrom), nameof(TermTo) }); } } }
Minimal APIでのモデルバリデーション
.NET 9 まで Minimal API ではモデルバリデーションを利用することができず、独自のバリデーションロジックを実装する必要がありました。
※私は Validator.TryValidateObject
メソッドを使っていました。
しかし先日リリースされた .NET 10 Preview 3 にて、ついにモデルバリデーションがサポートされるようになります。
実装方法はこちらのリリースノートに記載されていますが、いまいち分かりにくかったので解説します。
.NET 10 Preview 3 のインストール
このコードは .NET 10 Preview 3 を前提としていますので、まずは SDK をインストールしましょう。
winget install --id Microsoft.Dotnet.Sdk.Preview
バリデーション無しの基本構成
まず、検証用に Minimal API アプリケーションと Program.cs を準備します。
dotnet new webapi -f net10.0 -o MyApi
var builder = WebApplication.CreateBuilder(args); builder.Services.AddOpenApi(); var app = builder.Build(); app.MapOpenApi(); app.UseHttpsRedirection(); app.MapPost("/todoitems", (Todo todo) => { return Results.Created($"/todoitems/{todo.Id}", todo); }); app.Run(); class Todo { public int Id { get; set; } public string Name { get; set; } public bool IsComplete { get; set; } }
この構成ではまだバリデーションは行われず、名前 (name) を指定しなくてもリクエストは処理されます。
そのため、次のようなバリデーションの構成を適用します。
Program.cs
バリデーションのサポートを有効にします。 > AddValidation
メソッド
var builder = WebApplication.CreateBuilder(args); builder.Services.AddOpenApi(); builder.Services.AddValidation(); // ここ
プロジェクトファイル
csproj ファイルにコード生成を有効にする設定を入れます。 > <InterceptorsNamespaces>
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net10.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <!-- ここ --> <InterceptorsNamespaces>$(InterceptorsNamespaces);Microsoft.AspNetCore.Http.Validation.Generated</InterceptorsNamespaces> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0-preview.3.25172.1" /> </ItemGroup> </Project>
モデル
モデルクラスにバリデーションを定義します。
このとき、属性での検証と IValidatableObject
インターフェースのどちらも使用することができます。
class Todo : IValidatableObject { public int Id { get; set; } [Display(Name = "名前")] [Required(ErrorMessage = "{0}が指定されていません。")] public string? Name { get; set; } public bool IsComplete { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { if (!string.IsNullOrWhiteSpace(Name) && !IsComplete) { yield return new ValidationResult( "名前を指定したToDoは完了である必要があります。", new[] { nameof(Name), nameof(IsComplete) }); } } }
動作確認
モデルの定義に沿ったバリデーションが行われ、レスポンスにエラーメッセージが含まれるようになりました。
curl --location 'http://localhost:5247/todoitems/' ` --header 'Content-Type: application/json' ` --data '{ "id": 0, "name": null, "isComplete": true }' { "title": "One or more validation errors occurred.", "errors": { "Name": [ "名前が指定されていません。" ] } }
curl --location 'http://localhost:5247/todoitems/' ` --header 'Content-Type: application/json' ` --data '{ "id": 0, "name": "yuta", "isComplete": false }' { "title": "One or more validation errors occurred.", "errors": { "Name": [ "名前を指定したToDoは完了である必要があります。" ] } }
これで MVC や Razor Pages と同じように、DataAnnotation を使ったモデルバリデーションができるようになりました。
個人的にはこのアップデートだけでも .NET 10 を使う理由になると感じています。