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
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;
}
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).
Where checks run without your own call
@Validated()on a bean class checks the arguments and return values of its methods. A violation throws aConstraintViolationException. See Method validation.@Validated()on a@ConfigurationPropertiesclass checks the bound settings when the application starts. An invalid value stops the start. See Configuration.- In the web starter,
@Valid()on a@RequestBodyparameter 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: every built-in constraint annotation, its parameters, and how to write your own.
- Validator, Errors and DataBinder:
BeanValidator, hand-writtenValidators, theErrorsresult, and binding form input. - Method validation:
@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).
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:validateObjectreturns an empty list. - Constraint mistakes, such as
@Sizeon anint, 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.