0
# Validation
1
2
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.
3
4
## Capabilities
5
6
### Validation Framework Core
7
8
Central validation utilities and JSR-303 integration for comprehensive data validation.
9
10
```java { .api }
11
/**
12
* Validation helpers and JSR-303 integration
13
*/
14
public class Validation {
15
/** Get the underlying JSR-303 validator instance */
16
public static Validator getValidator();
17
}
18
19
/**
20
* Represents a form or field validation error
21
*/
22
public class ValidationError {
23
/** Create validation error with key and message */
24
public ValidationError(String key, String message);
25
26
/** Create validation error with message arguments */
27
public ValidationError(String key, String message, List<Object> arguments);
28
29
/** Create validation error with multiple messages */
30
public ValidationError(String key, List<String> messages, List<Object> arguments);
31
32
/** Get the field key or path */
33
public String key();
34
35
/** Get the primary error message */
36
public String message();
37
38
/** Get all error messages */
39
public List<String> messages();
40
41
/** Get message template arguments */
42
public List<Object> arguments();
43
}
44
```
45
46
### Built-in Validation Constraints
47
48
Comprehensive set of built-in validation constraints with factory methods for programmatic validation.
49
50
```java { .api }
51
/**
52
* Built-in validation constraints and validator factories
53
*/
54
public class Constraints {
55
/** Convert constraint descriptors to human-readable format */
56
public static List<Tuple<String,List<Object>>> displayableConstraint(Set<ConstraintDescriptor<?>> constraints);
57
58
/** Create required field validator */
59
public static Validator<Object> required();
60
61
/** Create minimum numeric value validator */
62
public static Validator<Number> min(long value);
63
64
/** Create maximum numeric value validator */
65
public static Validator<Number> max(long value);
66
67
/** Create minimum string length validator */
68
public static Validator<String> minLength(long value);
69
70
/** Create maximum string length validator */
71
public static Validator<String> maxLength(long value);
72
73
/** Create email format validator */
74
public static Validator<String> email();
75
76
/** Create regex pattern validator */
77
public static Validator<String> pattern(String regex);
78
}
79
80
/**
81
* Base class for all Play Framework validators
82
*/
83
public abstract class Constraints.Validator<T> {
84
/** Validate the given object */
85
public abstract boolean isValid(T object);
86
87
/** Validate with JSR-303 context */
88
public boolean isValid(T object, ConstraintValidatorContext constraintContext);
89
90
/** Get error message key and arguments */
91
public abstract Tuple<String, Object[]> getErrorMessageKey();
92
}
93
```
94
95
### Validation Annotations
96
97
Complete set of validation annotations for declarative model validation.
98
99
```java { .api }
100
/** Field is required and cannot be null or empty */
101
@interface Required {}
102
103
/** Numeric field must be at least the specified value */
104
@interface Min {
105
long value();
106
}
107
108
/** Numeric field must be at most the specified value */
109
@interface Max {
110
long value();
111
}
112
113
/** String field must have at least the specified length */
114
@interface MinLength {
115
long value();
116
}
117
118
/** String field must have at most the specified length */
119
@interface MaxLength {
120
long value();
121
}
122
123
/** String field must be a valid email address */
124
@interface Email {}
125
126
/** String field must match the specified regex pattern */
127
@interface Pattern {
128
String value();
129
String message() default "";
130
}
131
132
/** Use custom validator class for validation */
133
@interface ValidateWith {
134
Class<? extends Constraints.Validator> value();
135
}
136
```
137
138
**Usage Examples:**
139
140
```java
141
import play.data.validation.Constraints.*;
142
143
public class User {
144
@Required
145
@MinLength(2)
146
@MaxLength(50)
147
public String name;
148
149
@Required
150
151
public String email;
152
153
@Required
154
@MinLength(8)
155
@Pattern("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$")
156
public String password;
157
158
@Min(18)
159
@Max(120)
160
public Integer age;
161
162
@MaxLength(500)
163
public String bio;
164
}
165
166
public class Product {
167
@Required
168
public String name;
169
170
@Required
171
@Min(0)
172
public BigDecimal price;
173
174
@Required
175
@Pattern("^[A-Z]{2,3}-\\d{4}$") // Format: AB-1234
176
public String sku;
177
}
178
```
179
180
### Built-in Validator Implementations
181
182
Pre-built validator classes that power the validation annotations.
183
184
```java { .api }
185
/** Validates that a field is not null or empty */
186
public class RequiredValidator extends Constraints.Validator<Object> {
187
public boolean isValid(Object object);
188
public Tuple<String, Object[]> getErrorMessageKey();
189
}
190
191
/** Validates minimum numeric values */
192
public class MinValidator extends Constraints.Validator<Number> {
193
public MinValidator(long min);
194
public boolean isValid(Number value);
195
public Tuple<String, Object[]> getErrorMessageKey();
196
}
197
198
/** Validates maximum numeric values */
199
public class MaxValidator extends Constraints.Validator<Number> {
200
public MaxValidator(long max);
201
public boolean isValid(Number value);
202
public Tuple<String, Object[]> getErrorMessageKey();
203
}
204
205
/** Validates minimum string length */
206
public class MinLengthValidator extends Constraints.Validator<String> {
207
public MinLengthValidator(long minLength);
208
public boolean isValid(String value);
209
public Tuple<String, Object[]> getErrorMessageKey();
210
}
211
212
/** Validates maximum string length */
213
public class MaxLengthValidator extends Constraints.Validator<String> {
214
public MaxLengthValidator(long maxLength);
215
public boolean isValid(String value);
216
public Tuple<String, Object[]> getErrorMessageKey();
217
}
218
219
/** Validates email address format */
220
public class EmailValidator extends Constraints.Validator<String> {
221
public boolean isValid(String value);
222
public Tuple<String, Object[]> getErrorMessageKey();
223
}
224
225
/** Validates strings against regex patterns */
226
public class PatternValidator extends Constraints.Validator<String> {
227
public PatternValidator(String pattern);
228
public boolean isValid(String value);
229
public Tuple<String, Object[]> getErrorMessageKey();
230
}
231
232
/** Validates using custom validator classes */
233
public class ValidateWithValidator extends Constraints.Validator<Object> {
234
public ValidateWithValidator(Class<? extends Constraints.Validator> validatorClass);
235
public boolean isValid(Object value);
236
public Tuple<String, Object[]> getErrorMessageKey();
237
}
238
```
239
240
## Usage Examples
241
242
### Basic Model Validation
243
244
```java
245
import play.data.Form;
246
import play.data.validation.Constraints.*;
247
import play.mvc.Controller;
248
import play.mvc.Result;
249
250
public class RegistrationForm {
251
@Required
252
@MinLength(3)
253
@MaxLength(20)
254
public String username;
255
256
@Required
257
258
public String email;
259
260
@Required
261
@MinLength(8)
262
@Pattern("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]+$")
263
public String password;
264
265
@Required
266
@Min(13)
267
public Integer age;
268
269
@MaxLength(200)
270
public String bio;
271
}
272
273
public class UserController extends Controller {
274
public Result register() {
275
Form<RegistrationForm> form = Form.form(RegistrationForm.class).bindFromRequest();
276
277
if (form.hasErrors()) {
278
// Get all validation errors
279
Map<String, List<ValidationError>> errors = form.errors();
280
return badRequest(Json.toJson(errors));
281
}
282
283
RegistrationForm registration = form.get();
284
// Process valid registration
285
return ok("Registration successful");
286
}
287
}
288
```
289
290
### Custom Validation Logic
291
292
```java
293
import play.data.validation.Constraints.Validator;
294
295
// Custom validator for business logic
296
public class UniqueEmailValidator extends Constraints.Validator<String> {
297
298
@Override
299
public boolean isValid(String email) {
300
// Check if email already exists in database
301
return !userService.emailExists(email);
302
}
303
304
@Override
305
public Tuple<String, Object[]> getErrorMessageKey() {
306
return Tuple.create("validation.email.unique", new Object[]{});
307
}
308
}
309
310
// Using custom validator
311
public class User {
312
@Required
313
314
@ValidateWith(UniqueEmailValidator.class)
315
public String email;
316
}
317
```
318
319
### Validation Groups
320
321
```java
322
// Validation groups for different contexts
323
public interface CreateGroup {}
324
public interface UpdateGroup {}
325
326
public class User {
327
@Required(groups = {CreateGroup.class, UpdateGroup.class})
328
public String name;
329
330
@Required(groups = CreateGroup.class) // Only required on creation
331
332
public String email;
333
334
@MinLength(value = 8, groups = CreateGroup.class) // Only validate on creation
335
public String password;
336
}
337
338
// Using validation groups
339
public Result createUser() {
340
Form<User> form = Form.form("user", User.class, CreateGroup.class).bindFromRequest();
341
// Validation will only apply constraints in CreateGroup
342
}
343
344
public Result updateUser() {
345
Form<User> form = Form.form("user", User.class, UpdateGroup.class).bindFromRequest();
346
// Validation will only apply constraints in UpdateGroup
347
}
348
```
349
350
### Programmatic Validation
351
352
```java
353
import play.data.validation.Constraints;
354
355
public class ValidationService {
356
357
public Result validateUserData(String email, String password, Integer age) {
358
List<ValidationError> errors = new ArrayList<>();
359
360
// Validate email
361
if (!Constraints.email().isValid(email)) {
362
errors.add(new ValidationError("email", "Invalid email format"));
363
}
364
365
// Validate password length
366
if (!Constraints.minLength(8).isValid(password)) {
367
errors.add(new ValidationError("password", "Password must be at least 8 characters"));
368
}
369
370
// Validate age
371
if (!Constraints.min(18).isValid(age)) {
372
errors.add(new ValidationError("age", "Must be at least 18 years old"));
373
}
374
375
if (!errors.isEmpty()) {
376
return badRequest(Json.toJson(errors));
377
}
378
379
return ok("Validation passed");
380
}
381
}
382
```
383
384
### Error Message Customization
385
386
```java
387
// Custom error messages in model
388
public class Product {
389
@Required(message = "Product name is required")
390
@MinLength(value = 3, message = "Product name must be at least 3 characters")
391
public String name;
392
393
@Required(message = "Price is required")
394
@Min(value = 0, message = "Price must be positive")
395
public BigDecimal price;
396
}
397
398
// Accessing detailed error information
399
public Result processProduct() {
400
Form<Product> form = Form.form(Product.class).bindFromRequest();
401
402
if (form.hasErrors()) {
403
for (Map.Entry<String, List<ValidationError>> entry : form.errors().entrySet()) {
404
String field = entry.getKey();
405
for (ValidationError error : entry.getValue()) {
406
Logger.info("Field '{}': {}", field, error.message());
407
}
408
}
409
return badRequest(form.errorsAsJson());
410
}
411
412
return ok("Product is valid");
413
}
414
```
415
416
## Advanced Validation Patterns
417
418
### Conditional Validation
419
420
```java
421
public class ConditionalValidator extends Constraints.Validator<MyModel> {
422
@Override
423
public boolean isValid(MyModel model) {
424
// Validate field A only if field B has a specific value
425
if ("premium".equals(model.accountType)) {
426
return model.creditLimit != null && model.creditLimit > 0;
427
}
428
return true;
429
}
430
431
@Override
432
public Tuple<String, Object[]> getErrorMessageKey() {
433
return Tuple.create("validation.conditional.failed", new Object[]{});
434
}
435
}
436
```
437
438
### Cross-Field Validation
439
440
```java
441
public class PasswordConfirmationValidator extends Constraints.Validator<PasswordForm> {
442
@Override
443
public boolean isValid(PasswordForm form) {
444
return Objects.equals(form.password, form.confirmPassword);
445
}
446
447
@Override
448
public Tuple<String, Object[]> getErrorMessageKey() {
449
return Tuple.create("validation.password.mismatch", new Object[]{});
450
}
451
}
452
453
public class PasswordForm {
454
@Required
455
@MinLength(8)
456
public String password;
457
458
@Required
459
public String confirmPassword;
460
461
@ValidateWith(PasswordConfirmationValidator.class)
462
public PasswordForm getThis() {
463
return this;
464
}
465
}
466
```