or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdconfiguration.mdcore-application.mddatabase.mdindex.mdmetrics.mdrest-api.mdtesting.mdvalidation.md

validation.mddocs/

0

# Validation

1

2

Comprehensive input validation using Bean Validation (JSR-303) with custom Dropwizard validators for durations, data sizes, and other common types.

3

4

## Capabilities

5

6

### Bean Validation

7

8

Standard JSR-303 Bean Validation annotations for validating method parameters, request bodies, and configuration objects.

9

10

```java { .api }

11

// Standard validation annotations

12

@NotNull // Value must not be null

13

@NotEmpty // String/Collection must not be null or empty

14

@NotBlank // String must not be null, empty, or whitespace only

15

@Size(min = 1, max = 100) // Collection/String size constraints

16

@Min(1) // Numeric minimum value

17

@Max(100) // Numeric maximum value

18

@Range(min = 1, max = 100) // Numeric range

19

@Pattern(regexp = "\\d+") // Regular expression pattern

20

@Email // Valid email address format

21

@Valid // Cascade validation to nested objects

22

23

// Usage in JAX-RS resources

24

@POST

25

public Response createUser(@Valid @NotNull User user) {

26

// user is automatically validated before method execution

27

}

28

29

@GET

30

public User getUser(@PathParam("id") @NotNull @Min(1) Long id) {

31

// id parameter is validated

32

}

33

```

34

35

### Dropwizard Validation Annotations

36

37

Custom validation annotations specific to Dropwizard for common application configuration and input validation scenarios.

38

39

```java { .api }

40

package io.dropwizard.validation;

41

42

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})

43

@Retention(RetentionPolicy.RUNTIME)

44

@Constraint(validatedBy = DurationRangeValidator.class)

45

public @interface DurationRange {

46

/**

47

* Minimum duration value.

48

*/

49

long min() default 0;

50

51

/**

52

* Maximum duration value.

53

*/

54

long max() default Long.MAX_VALUE;

55

56

/**

57

* Time unit for min value.

58

*/

59

TimeUnit minUnit() default TimeUnit.MILLISECONDS;

60

61

/**

62

* Time unit for max value.

63

*/

64

TimeUnit maxUnit() default TimeUnit.MILLISECONDS;

65

}

66

67

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})

68

@Retention(RetentionPolicy.RUNTIME)

69

@Constraint(validatedBy = MinDurationValidator.class)

70

public @interface MinDuration {

71

/**

72

* Minimum duration value.

73

*/

74

long value();

75

76

/**

77

* Time unit for the value.

78

*/

79

TimeUnit unit() default TimeUnit.MILLISECONDS;

80

}

81

82

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})

83

@Retention(RetentionPolicy.RUNTIME)

84

@Constraint(validatedBy = MaxDurationValidator.class)

85

public @interface MaxDuration {

86

/**

87

* Maximum duration value.

88

*/

89

long value();

90

91

/**

92

* Time unit for the value.

93

*/

94

TimeUnit unit() default TimeUnit.MILLISECONDS;

95

}

96

```

97

98

**Usage Example:**

99

100

```java

101

public class ServerConfiguration {

102

@DurationRange(min = 1, minUnit = TimeUnit.SECONDS, max = 30, maxUnit = TimeUnit.SECONDS)

103

private Duration connectionTimeout = Duration.seconds(5);

104

105

@MinDuration(value = 100, unit = TimeUnit.MILLISECONDS)

106

private Duration requestTimeout = Duration.milliseconds(500);

107

108

@MaxDuration(value = 1, unit = TimeUnit.HOURS)

109

private Duration sessionTimeout = Duration.minutes(30);

110

}

111

```

112

113

### Data Size Validation

114

115

Validation annotations for data size constraints with support for various units (bytes, KB, MB, GB).

116

117

```java { .api }

118

package io.dropwizard.validation;

119

120

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})

121

@Retention(RetentionPolicy.RUNTIME)

122

@Constraint(validatedBy = DataSizeRangeValidator.class)

123

public @interface DataSizeRange {

124

/**

125

* Minimum data size value.

126

*/

127

long min() default 0;

128

129

/**

130

* Maximum data size value.

131

*/

132

long max() default Long.MAX_VALUE;

133

134

/**

135

* Unit for min value.

136

*/

137

DataSize.Unit minUnit() default DataSize.Unit.BYTES;

138

139

/**

140

* Unit for max value.

141

*/

142

DataSize.Unit maxUnit() default DataSize.Unit.BYTES;

143

}

144

145

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})

146

@Retention(RetentionPolicy.RUNTIME)

147

@Constraint(validatedBy = MinDataSizeValidator.class)

148

public @interface MinDataSize {

149

/**

150

* Minimum data size value.

151

*/

152

long value();

153

154

/**

155

* Unit for the value.

156

*/

157

DataSize.Unit unit() default DataSize.Unit.BYTES;

158

}

159

160

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})

161

@Retention(RetentionPolicy.RUNTIME)

162

@Constraint(validatedBy = MaxDataSizeValidator.class)

163

public @interface MaxDataSize {

164

/**

165

* Maximum data size value.

166

*/

167

long value();

168

169

/**

170

* Unit for the value.

171

*/

172

DataSize.Unit unit() default DataSize.Unit.BYTES;

173

}

174

```

175

176

**Usage Example:**

177

178

```java

179

public class FileUploadConfiguration {

180

@DataSizeRange(min = 1, minUnit = DataSize.Unit.KILOBYTES,

181

max = 10, maxUnit = DataSize.Unit.MEGABYTES)

182

private DataSize maxFileSize = DataSize.megabytes(5);

183

184

@MinDataSize(value = 512, unit = DataSize.Unit.BYTES)

185

private DataSize bufferSize = DataSize.kilobytes(8);

186

187

@MaxDataSize(value = 100, unit = DataSize.Unit.MEGABYTES)

188

private DataSize totalUploadLimit = DataSize.megabytes(50);

189

}

190

```

191

192

### Enum and Choice Validation

193

194

Validation for restricting values to a specific set of allowed options.

195

196

```java { .api }

197

package io.dropwizard.validation;

198

199

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})

200

@Retention(RetentionPolicy.RUNTIME)

201

@Constraint(validatedBy = OneOfValidator.class)

202

public @interface OneOf {

203

/**

204

* Array of allowed values.

205

*/

206

String[] value();

207

208

/**

209

* Whether comparison should be case insensitive.

210

*/

211

boolean ignoreCase() default false;

212

213

/**

214

* Whether whitespace should be ignored.

215

*/

216

boolean ignoreWhitespace() default false;

217

}

218

```

219

220

**Usage Example:**

221

222

```java

223

public class ApplicationConfiguration {

224

@OneOf({"development", "staging", "production"})

225

private String environment = "development";

226

227

@OneOf(value = {"DEBUG", "INFO", "WARN", "ERROR"}, ignoreCase = true)

228

private String logLevel = "INFO";

229

230

@OneOf({"http", "https"})

231

private String protocol = "http";

232

}

233

```

234

235

### Port Range Validation

236

237

Validation for network port numbers with configurable ranges.

238

239

```java { .api }

240

package io.dropwizard.validation;

241

242

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})

243

@Retention(RetentionPolicy.RUNTIME)

244

@Constraint(validatedBy = PortRangeValidator.class)

245

public @interface PortRange {

246

/**

247

* Minimum port number.

248

*/

249

int min() default 1;

250

251

/**

252

* Maximum port number.

253

*/

254

int max() default 65535;

255

}

256

```

257

258

**Usage Example:**

259

260

```java

261

public class ServerConfiguration {

262

@PortRange(min = 1024, max = 65535)

263

private int applicationPort = 8080;

264

265

@PortRange(min = 8000, max = 9000)

266

private int adminPort = 8081;

267

}

268

```

269

270

### Self-Validating Objects

271

272

Custom validation methods for complex business logic validation that cannot be expressed with simple annotations.

273

274

```java { .api }

275

package io.dropwizard.validation.selfvalidating;

276

277

@Target({ElementType.TYPE})

278

@Retention(RetentionPolicy.RUNTIME)

279

public @interface SelfValidating {

280

}

281

282

@Target({ElementType.METHOD})

283

@Retention(RetentionPolicy.RUNTIME)

284

public @interface ValidationMethod {

285

/**

286

* Error message when validation fails.

287

*/

288

String message();

289

290

/**

291

* Groups for conditional validation.

292

*/

293

Class<?>[] groups() default {};

294

}

295

```

296

297

**Usage Example:**

298

299

```java

300

@SelfValidating

301

public class UserRegistration {

302

@NotEmpty

303

private String username;

304

305

@NotEmpty

306

private String password;

307

308

@NotEmpty

309

private String confirmPassword;

310

311

@Email

312

private String email;

313

314

private int age;

315

316

@ValidationMethod(message = "Passwords do not match")

317

public boolean isPasswordValid() {

318

return password != null && password.equals(confirmPassword);

319

}

320

321

@ValidationMethod(message = "User must be at least 13 years old")

322

public boolean isAgeValid() {

323

return age >= 13;

324

}

325

326

@ValidationMethod(message = "Username cannot be the same as email")

327

public boolean isUsernameDistinct() {

328

return !username.equals(email);

329

}

330

}

331

```

332

333

### Validation Groups

334

335

Conditional validation using groups to apply different validation rules in different contexts.

336

337

```java { .api }

338

// Validation group interfaces

339

public interface CreateValidation {}

340

public interface UpdateValidation {}

341

342

public class User {

343

@NotNull(groups = {CreateValidation.class, UpdateValidation.class})

344

private String name;

345

346

@NotNull(groups = CreateValidation.class)

347

@Email(groups = {CreateValidation.class, UpdateValidation.class})

348

private String email;

349

350

@Null(groups = CreateValidation.class)

351

@NotNull(groups = UpdateValidation.class)

352

private Long id;

353

}

354

355

// Usage in resources

356

@POST

357

public User createUser(@Valid(CreateValidation.class) User user) {

358

// Only CreateValidation constraints are applied

359

}

360

361

@PUT

362

@Path("/{id}")

363

public User updateUser(@PathParam("id") Long id,

364

@Valid(UpdateValidation.class) User user) {

365

// Only UpdateValidation constraints are applied

366

}

367

```

368

369

### Custom Validators

370

371

Creating custom validation annotations and validators for application-specific validation requirements.

372

373

```java { .api }

374

// Custom validation annotation

375

@Target({ElementType.FIELD, ElementType.PARAMETER})

376

@Retention(RetentionPolicy.RUNTIME)

377

@Constraint(validatedBy = CreditCardValidator.class)

378

public @interface CreditCard {

379

String message() default "Invalid credit card number";

380

Class<?>[] groups() default {};

381

Class<? extends Payload>[] payload() default {};

382

383

CreditCardType[] acceptedTypes() default {CreditCardType.VISA, CreditCardType.MASTERCARD};

384

385

enum CreditCardType {

386

VISA, MASTERCARD, AMEX, DISCOVER

387

}

388

}

389

390

// Custom validator implementation

391

public class CreditCardValidator implements ConstraintValidator<CreditCard, String> {

392

private CreditCard.CreditCardType[] acceptedTypes;

393

394

@Override

395

public void initialize(CreditCard constraintAnnotation) {

396

this.acceptedTypes = constraintAnnotation.acceptedTypes();

397

}

398

399

@Override

400

public boolean isValid(String value, ConstraintValidatorContext context) {

401

if (value == null) {

402

return true; // Use @NotNull for null checks

403

}

404

405

// Remove spaces and hyphens

406

String cleanNumber = value.replaceAll("[\\s-]", "");

407

408

// Validate using Luhn algorithm

409

if (!isValidLuhn(cleanNumber)) {

410

return false;

411

}

412

413

// Check card type

414

CreditCard.CreditCardType type = detectCardType(cleanNumber);

415

return Arrays.asList(acceptedTypes).contains(type);

416

}

417

418

private boolean isValidLuhn(String number) {

419

// Luhn algorithm implementation

420

int sum = 0;

421

boolean alternate = false;

422

for (int i = number.length() - 1; i >= 0; i--) {

423

int digit = Character.getNumericValue(number.charAt(i));

424

if (alternate) {

425

digit *= 2;

426

if (digit > 9) {

427

digit = (digit % 10) + 1;

428

}

429

}

430

sum += digit;

431

alternate = !alternate;

432

}

433

return (sum % 10) == 0;

434

}

435

436

private CreditCard.CreditCardType detectCardType(String number) {

437

if (number.startsWith("4")) {

438

return CreditCard.CreditCardType.VISA;

439

} else if (number.startsWith("5")) {

440

return CreditCard.CreditCardType.MASTERCARD;

441

} else if (number.startsWith("34") || number.startsWith("37")) {

442

return CreditCard.CreditCardType.AMEX;

443

} else if (number.startsWith("6")) {

444

return CreditCard.CreditCardType.DISCOVER;

445

}

446

return CreditCard.CreditCardType.VISA; // Default

447

}

448

}

449

```

450

451

## Validation Error Handling

452

453

### Validation Exception Mapping

454

455

Handling validation errors and converting them to appropriate HTTP responses with detailed error information.

456

457

```java

458

@Provider

459

public class ValidationExceptionMapper implements ExceptionMapper<ConstraintViolationException> {

460

@Override

461

public Response toResponse(ConstraintViolationException exception) {

462

List<String> errors = exception.getConstraintViolations()

463

.stream()

464

.map(violation -> violation.getPropertyPath() + ": " + violation.getMessage())

465

.collect(Collectors.toList());

466

467

ErrorResponse errorResponse = new ErrorResponse("Validation failed", errors);

468

469

return Response.status(Response.Status.BAD_REQUEST)

470

.entity(errorResponse)

471

.type(MediaType.APPLICATION_JSON)

472

.build();

473

}

474

}

475

476

public class ErrorResponse {

477

private String message;

478

private List<String> errors;

479

480

public ErrorResponse(String message, List<String> errors) {

481

this.message = message;

482

this.errors = errors;

483

}

484

485

// getters and setters

486

}

487

```