
# Validation

バリデーションは、オブジェクトの値が受け入れ可能かどうかを確認し、見つかった問題をすべて報告する仕組みです。

## なぜ必要か

入力はさまざまな場所から来ます。リクエストボディ、フォーム、設定ファイル、キューからのメッセージなどです。それぞれの値には確認が必要です。名前が空白でないこと、年齢が 18 以上であること、メールアドレスがその形をしていることなどです。こうした確認を `if` 文で毎回書くのは繰り返しが多く、書き忘れも起きやすくなります。

バリデーションでは、ルールをプロパティのアノテーションとして一度だけ書きます。バリデータがアノテーションを読み、破られたルールをすべて報告します。各エラーにはエラーコードが付くので、メッセージファイルでユーザー向けの文に変えられます。

自分のコードが生成し、すでに信頼できる値には必要ありません。型の確認にも必要ありません。それは Dart の型システムが行います。

## 短い例

```dart
import 'package:spring_core_dart/spring_core_dart.dart';

// @Introspected lets the generator describe the properties,
// so the validator can read them without reflection.
@Introspected()
class SignupForm {
  @NotBlank()
  @Size(max: 20)
  String name = '';

  @NotNull()
  @Email()
  String? email;

  @Min(18)
  int age = 0;
}
```

```dart
import 'package:my_app/src/beans.dart';
import 'package:my_app/src/signup_form.dart';
import 'package:spring_core_dart/spring_core_dart.dart';

const log = SpringLog('signup');

void main() {
  final context = ApplicationContext.run([appBeans]);

  // Use the context's introspector: it knows the application's classes.
  final validator = BeanValidator(
    introspector: context.getBean<BeanIntrospector>(),
  );

  final form = SignupForm()
    ..name = 'Ada'
    ..age = 17;

  for (final violation in validator.validateObject(form)) {
    // email: must not be null
    // age: must be greater than or equal to 18
    log.warning('${violation.propertyPath}: ${violation.message}');
  }
  context.close();
}
```

モジュール `appBeans` はアプリケーションで一度だけ `@ComponentScan() final BeanModule appBeans = $appBeans;` として宣言します([Beans](../beans/index.md) を参照)。

## 自分で呼ばなくても検証が行われる場所

- Bean クラスに付けた `@Validated()` は、そのメソッドの引数と戻り値を検証します。違反があると `ConstraintViolationException` が投げられます。[メソッドバリデーション](method-validation.md) を参照してください。
- `@ConfigurationProperties` クラスに付けた `@Validated()` は、アプリケーションの起動時にバインドされた設定値を検証します。不正な値があると起動が止まります。[Configuration](../config/index.md) を参照してください。
- Web スターターでは、`@RequestBody` パラメータに付けた `@Valid()` がボディを検証します。違反があると、フィールドを列挙した 400 レスポンスが返ります。

それ以外の場合は、`BeanValidator` を自分で呼ぶか、`DataBinder` を使って文字列の入力をオブジェクトにコピーし、同時に検証します。

## このセクションのページ

- [Constraints](constraints.md): 組み込みの制約アノテーションすべてと、そのパラメータ、独自の制約の書き方。
- [Validator, Errors and DataBinder](validator.md): `BeanValidator`、手書きの `Validator`、結果を表す `Errors`、フォーム入力のバインド。
- [メソッドバリデーション](method-validation.md): 呼び出しのたびに引数と戻り値を検証する `@Validated()` Bean。

## Spring (Java) では

Java で対応するのは `org.springframework.validation` と Jakarta Bean Validation(通常は Hibernate Validator)の組み合わせです。

```java
class SignupForm {
  @NotBlank @Size(max = 20) private String name;
  @NotNull @Email private String email;
  @Min(18) private int age;
}

Set<ConstraintViolation<SignupForm>> violations = validator.validate(form);
```

重要な違い:

- クラスには `@Introspected()` が必要です。ジェネレータがビルド時にプロパティを記述します。これがないクラスは検証されず、`validateObject` は空のリストを返します。
- `int` に付けた `@Size` のような制約の誤りは、実行時ではなくビルド時にエラーになります。
- 3 つの制約は、Java の名前が Dart ですでに使われているため別のクラス名です。`@MustBeNull`(Jakarta の `@Null`)、`@Matches`(`@Pattern`)、`@FutureDate`(`@Future`)。エラーコードは Jakarta の名前のままです。

## 関連項目

- [Beans](../beans/index.md)
- [Configuration](../config/index.md)
- [型変換](../conversion/index.md)
