
# Validation

Validation checks that the values of an object are acceptable and reports every problem it finds.

## Why you need it

Input comes from many places: a request body, a form, a settings file, a message from a queue. Each value needs checks: a name is not blank, an age is at least 18, an email address looks like one. Writing these checks as `if` statements in every place is repetitive and easy to forget.

With validation you write the rule once, as an annotation on the property. A validator reads the annotations and reports every broken rule, with an error code that a message file can turn into text for the user.

You do not need it for values that your own code produces and already trusts. You also do not need it for type checks: Dart's type system handles those.

## A short example

```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();
}
```

The module `appBeans` is declared once in the application, as `@ComponentScan() final BeanModule appBeans = $appBeans;` (see [Beans](../beans/index.md)).

## Where checks run without your own call

- `@Validated()` on a bean class checks the arguments and return values of its methods. A violation throws a `ConstraintViolationException`. See [Method validation](method-validation.md).
- `@Validated()` on a `@ConfigurationProperties` class checks the bound settings when the application starts. An invalid value stops the start. See [Configuration](../config/index.md).
- In the web starter, `@Valid()` on a `@RequestBody` parameter checks the body. A violation is answered with a 400 response that lists the fields.

For everything else, call `BeanValidator` yourself, or use `DataBinder` to copy string input onto an object and check it in one step.

## Pages in this section

- [Constraints](constraints.md): every built-in constraint annotation, its parameters, and how to write your own.
- [Validator, Errors and DataBinder](validator.md): `BeanValidator`, hand-written `Validator`s, the `Errors` result, and binding form input.
- [Method validation](method-validation.md): `@Validated()` beans that check their arguments and return values on every call.

## In Spring (Java)

The Java counterpart is `org.springframework.validation` together with Jakarta Bean Validation (usually 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);
```

Differences that matter:

- A class must be annotated `@Introspected()`. The generator then describes its properties at build time. Classes without it are not checked: `validateObject` returns an empty list.
- Constraint mistakes, such as `@Size` on an `int`, fail the build instead of failing at run time.
- Three constraints have other class names because Dart already uses the Java names: `@MustBeNull` (Jakarta `@Null`), `@Matches` (`@Pattern`) and `@FutureDate` (`@Future`). Their error codes keep the Jakarta names.

## See also

- [Beans](../beans/index.md)
- [Configuration](../config/index.md)
- [Type conversion](../conversion/index.md)
