Java API for the Play Framework providing web application development capabilities including form handling, validation, dependency injection, and utility libraries
—
Play Framework's validation system provides comprehensive form and data validation capabilities using JSR-303 Bean Validation with built-in constraints, custom validators, and detailed error reporting. The validation framework integrates seamlessly with form processing and provides both annotation-based and programmatic validation approaches.
Central validation utilities and JSR-303 integration for comprehensive data validation.
/**
* Validation helpers and JSR-303 integration
*/
public class Validation {
/** Get the underlying JSR-303 validator instance */
public static Validator getValidator();
}
/**
* Represents a form or field validation error
*/
public class ValidationError {
/** Create validation error with key and message */
public ValidationError(String key, String message);
/** Create validation error with message arguments */
public ValidationError(String key, String message, List<Object> arguments);
/** Create validation error with multiple messages */
public ValidationError(String key, List<String> messages, List<Object> arguments);
/** Get the field key or path */
public String key();
/** Get the primary error message */
public String message();
/** Get all error messages */
public List<String> messages();
/** Get message template arguments */
public List<Object> arguments();
}Comprehensive set of built-in validation constraints with factory methods for programmatic validation.
/**
* Built-in validation constraints and validator factories
*/
public class Constraints {
/** Convert constraint descriptors to human-readable format */
public static List<Tuple<String,List<Object>>> displayableConstraint(Set<ConstraintDescriptor<?>> constraints);
/** Create required field validator */
public static Validator<Object> required();
/** Create minimum numeric value validator */
public static Validator<Number> min(long value);
/** Create maximum numeric value validator */
public static Validator<Number> max(long value);
/** Create minimum string length validator */
public static Validator<String> minLength(long value);
/** Create maximum string length validator */
public static Validator<String> maxLength(long value);
/** Create email format validator */
public static Validator<String> email();
/** Create regex pattern validator */
public static Validator<String> pattern(String regex);
}
/**
* Base class for all Play Framework validators
*/
public abstract class Constraints.Validator<T> {
/** Validate the given object */
public abstract boolean isValid(T object);
/** Validate with JSR-303 context */
public boolean isValid(T object, ConstraintValidatorContext constraintContext);
/** Get error message key and arguments */
public abstract Tuple<String, Object[]> getErrorMessageKey();
}Complete set of validation annotations for declarative model validation.
/** Field is required and cannot be null or empty */
@interface Required {}
/** Numeric field must be at least the specified value */
@interface Min {
long value();
}
/** Numeric field must be at most the specified value */
@interface Max {
long value();
}
/** String field must have at least the specified length */
@interface MinLength {
long value();
}
/** String field must have at most the specified length */
@interface MaxLength {
long value();
}
/** String field must be a valid email address */
@interface Email {}
/** String field must match the specified regex pattern */
@interface Pattern {
String value();
String message() default "";
}
/** Use custom validator class for validation */
@interface ValidateWith {
Class<? extends Constraints.Validator> value();
}Usage Examples:
import play.data.validation.Constraints.*;
public class User {
@Required
@MinLength(2)
@MaxLength(50)
public String name;
@Required
@Email
public String email;
@Required
@MinLength(8)
@Pattern("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$")
public String password;
@Min(18)
@Max(120)
public Integer age;
@MaxLength(500)
public String bio;
}
public class Product {
@Required
public String name;
@Required
@Min(0)
public BigDecimal price;
@Required
@Pattern("^[A-Z]{2,3}-\\d{4}$") // Format: AB-1234
public String sku;
}Pre-built validator classes that power the validation annotations.
/** Validates that a field is not null or empty */
public class RequiredValidator extends Constraints.Validator<Object> {
public boolean isValid(Object object);
public Tuple<String, Object[]> getErrorMessageKey();
}
/** Validates minimum numeric values */
public class MinValidator extends Constraints.Validator<Number> {
public MinValidator(long min);
public boolean isValid(Number value);
public Tuple<String, Object[]> getErrorMessageKey();
}
/** Validates maximum numeric values */
public class MaxValidator extends Constraints.Validator<Number> {
public MaxValidator(long max);
public boolean isValid(Number value);
public Tuple<String, Object[]> getErrorMessageKey();
}
/** Validates minimum string length */
public class MinLengthValidator extends Constraints.Validator<String> {
public MinLengthValidator(long minLength);
public boolean isValid(String value);
public Tuple<String, Object[]> getErrorMessageKey();
}
/** Validates maximum string length */
public class MaxLengthValidator extends Constraints.Validator<String> {
public MaxLengthValidator(long maxLength);
public boolean isValid(String value);
public Tuple<String, Object[]> getErrorMessageKey();
}
/** Validates email address format */
public class EmailValidator extends Constraints.Validator<String> {
public boolean isValid(String value);
public Tuple<String, Object[]> getErrorMessageKey();
}
/** Validates strings against regex patterns */
public class PatternValidator extends Constraints.Validator<String> {
public PatternValidator(String pattern);
public boolean isValid(String value);
public Tuple<String, Object[]> getErrorMessageKey();
}
/** Validates using custom validator classes */
public class ValidateWithValidator extends Constraints.Validator<Object> {
public ValidateWithValidator(Class<? extends Constraints.Validator> validatorClass);
public boolean isValid(Object value);
public Tuple<String, Object[]> getErrorMessageKey();
}import play.data.Form;
import play.data.validation.Constraints.*;
import play.mvc.Controller;
import play.mvc.Result;
public class RegistrationForm {
@Required
@MinLength(3)
@MaxLength(20)
public String username;
@Required
@Email
public String email;
@Required
@MinLength(8)
@Pattern("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]+$")
public String password;
@Required
@Min(13)
public Integer age;
@MaxLength(200)
public String bio;
}
public class UserController extends Controller {
public Result register() {
Form<RegistrationForm> form = Form.form(RegistrationForm.class).bindFromRequest();
if (form.hasErrors()) {
// Get all validation errors
Map<String, List<ValidationError>> errors = form.errors();
return badRequest(Json.toJson(errors));
}
RegistrationForm registration = form.get();
// Process valid registration
return ok("Registration successful");
}
}import play.data.validation.Constraints.Validator;
// Custom validator for business logic
public class UniqueEmailValidator extends Constraints.Validator<String> {
@Override
public boolean isValid(String email) {
// Check if email already exists in database
return !userService.emailExists(email);
}
@Override
public Tuple<String, Object[]> getErrorMessageKey() {
return Tuple.create("validation.email.unique", new Object[]{});
}
}
// Using custom validator
public class User {
@Required
@Email
@ValidateWith(UniqueEmailValidator.class)
public String email;
}// Validation groups for different contexts
public interface CreateGroup {}
public interface UpdateGroup {}
public class User {
@Required(groups = {CreateGroup.class, UpdateGroup.class})
public String name;
@Required(groups = CreateGroup.class) // Only required on creation
@Email
public String email;
@MinLength(value = 8, groups = CreateGroup.class) // Only validate on creation
public String password;
}
// Using validation groups
public Result createUser() {
Form<User> form = Form.form("user", User.class, CreateGroup.class).bindFromRequest();
// Validation will only apply constraints in CreateGroup
}
public Result updateUser() {
Form<User> form = Form.form("user", User.class, UpdateGroup.class).bindFromRequest();
// Validation will only apply constraints in UpdateGroup
}import play.data.validation.Constraints;
public class ValidationService {
public Result validateUserData(String email, String password, Integer age) {
List<ValidationError> errors = new ArrayList<>();
// Validate email
if (!Constraints.email().isValid(email)) {
errors.add(new ValidationError("email", "Invalid email format"));
}
// Validate password length
if (!Constraints.minLength(8).isValid(password)) {
errors.add(new ValidationError("password", "Password must be at least 8 characters"));
}
// Validate age
if (!Constraints.min(18).isValid(age)) {
errors.add(new ValidationError("age", "Must be at least 18 years old"));
}
if (!errors.isEmpty()) {
return badRequest(Json.toJson(errors));
}
return ok("Validation passed");
}
}// Custom error messages in model
public class Product {
@Required(message = "Product name is required")
@MinLength(value = 3, message = "Product name must be at least 3 characters")
public String name;
@Required(message = "Price is required")
@Min(value = 0, message = "Price must be positive")
public BigDecimal price;
}
// Accessing detailed error information
public Result processProduct() {
Form<Product> form = Form.form(Product.class).bindFromRequest();
if (form.hasErrors()) {
for (Map.Entry<String, List<ValidationError>> entry : form.errors().entrySet()) {
String field = entry.getKey();
for (ValidationError error : entry.getValue()) {
Logger.info("Field '{}': {}", field, error.message());
}
}
return badRequest(form.errorsAsJson());
}
return ok("Product is valid");
}public class ConditionalValidator extends Constraints.Validator<MyModel> {
@Override
public boolean isValid(MyModel model) {
// Validate field A only if field B has a specific value
if ("premium".equals(model.accountType)) {
return model.creditLimit != null && model.creditLimit > 0;
}
return true;
}
@Override
public Tuple<String, Object[]> getErrorMessageKey() {
return Tuple.create("validation.conditional.failed", new Object[]{});
}
}public class PasswordConfirmationValidator extends Constraints.Validator<PasswordForm> {
@Override
public boolean isValid(PasswordForm form) {
return Objects.equals(form.password, form.confirmPassword);
}
@Override
public Tuple<String, Object[]> getErrorMessageKey() {
return Tuple.create("validation.password.mismatch", new Object[]{});
}
}
public class PasswordForm {
@Required
@MinLength(8)
public String password;
@Required
public String confirmPassword;
@ValidateWith(PasswordConfirmationValidator.class)
public PasswordForm getThis() {
return this;
}
}Install with Tessl CLI
npx tessl i tessl/maven-com-typesafe-play--play-java