or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

dependency-injection.mdform-processing.mdformatting.mdindex.mdrouting.mdstreaming.mdutilities.mdvalidation.md

validation.mddocs/

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

@Email

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

@Email

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

@Email

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

@Email

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

```