0
# Validation
1
2
Hibernate Validator integration providing comprehensive validation support for JAX-RS resources with custom parameter extractors and constraint violation handling. Enables Bean Validation annotations on resource methods and request objects.
3
4
## Capabilities
5
6
### Validators Utility
7
8
Utility class for creating and configuring Hibernate validators with Dropwizard-specific enhancements.
9
10
```java { .api }
11
/**
12
* Utility class for Hibernate Validator configuration
13
* Provides factory methods for creating properly configured validators
14
*/
15
public class Validators {
16
17
/** Creates a new Validator with Dropwizard configuration */
18
public static Validator newValidator();
19
20
/** Creates a new ValidatorFactory with Dropwizard configuration */
21
public static ValidatorFactory newValidatorFactory();
22
23
/** Creates a new HibernateValidatorConfiguration with custom extractors */
24
public static HibernateValidatorConfiguration newConfiguration();
25
}
26
```
27
28
**Usage Examples:**
29
30
```java
31
import io.dropwizard.jersey.validation.Validators;
32
import jakarta.validation.Validator;
33
import jakarta.validation.ConstraintViolation;
34
import java.util.Set;
35
36
public class ValidationService {
37
38
private final Validator validator = Validators.newValidator();
39
40
public <T> void validate(T object) {
41
Set<ConstraintViolation<T>> violations = validator.validate(object);
42
if (!violations.isEmpty()) {
43
throw new ValidationException("Validation failed", violations);
44
}
45
}
46
47
public <T> void validateProperty(T object, String propertyName) {
48
Set<ConstraintViolation<T>> violations =
49
validator.validateProperty(object, propertyName);
50
if (!violations.isEmpty()) {
51
throw new ValidationException("Property validation failed", violations);
52
}
53
}
54
}
55
```
56
57
### Jersey Validation Exception
58
59
Exception class for Jersey-specific validation violations with detailed constraint violation information.
60
61
```java { .api }
62
/**
63
* Exception for Jersey validation violations
64
* Wraps constraint violations from Bean Validation
65
*/
66
public class JerseyViolationException extends ValidationException {
67
68
/** Creates exception with constraint violations */
69
public JerseyViolationException(Set<ConstraintViolation<?>> violations);
70
71
/** Gets the constraint violations that caused this exception */
72
public Set<ConstraintViolation<?>> getConstraintViolations();
73
74
/** Gets formatted error messages from constraint violations */
75
public List<String> getErrorMessages();
76
}
77
```
78
79
### Validation Exception Mapping
80
81
Exception mapper that converts validation exceptions to appropriate HTTP responses with detailed error information.
82
83
```java { .api }
84
/**
85
* Exception mapper for JerseyViolationException
86
* Converts validation violations to HTTP 422 responses with error details
87
*/
88
public class JerseyViolationExceptionMapper implements ExceptionMapper<JerseyViolationException> {
89
90
/** Maps JerseyViolationException to HTTP response with validation errors */
91
public Response toResponse(JerseyViolationException exception);
92
93
/** Creates validation error response with detailed field errors */
94
protected ValidationErrorMessage createErrorMessage(JerseyViolationException exception);
95
}
96
```
97
98
### Validation Error Message
99
100
Structured error message format for validation failures with field-level error details.
101
102
```java { .api }
103
/**
104
* Structured validation error message with field-level errors
105
* Provides detailed information about validation failures
106
*/
107
public class ValidationErrorMessage {
108
109
/** Creates validation error message from error list */
110
public ValidationErrorMessage(Collection<String> errors);
111
112
/** Gets list of validation error messages */
113
public List<String> getErrors();
114
115
/** Gets validation errors grouped by field name */
116
public Map<String, List<String>> getFieldErrors();
117
}
118
119
/**
120
* Individual constraint violation message with field and error details
121
*/
122
public class ConstraintMessage {
123
124
/** Creates constraint message from violation */
125
public ConstraintMessage(ConstraintViolation<?> violation);
126
127
/** Gets the field or property path that failed validation */
128
public String getPath();
129
130
/** Gets the validation error message */
131
public String getMessage();
132
133
/** Gets the invalid value that caused the violation */
134
public Object getInvalidValue();
135
136
/** Gets the constraint annotation type */
137
public String getConstraintType();
138
}
139
```
140
141
### Parameter Value Extractors
142
143
Custom value extractors that enable validation on Dropwizard parameter types.
144
145
```java { .api }
146
/**
147
* Value extractor for AbstractParam types
148
* Enables Bean Validation on parameter wrapper classes
149
* @param <T> the wrapped parameter type
150
*/
151
public class ParamValueExtractor<T> implements ValueExtractor<AbstractParam<@ExtractedValue T>> {
152
153
/** Extracts the wrapped value for validation */
154
public void extractValues(AbstractParam<T> originalValue, ValueReceiver receiver);
155
156
/** Descriptor for registering the extractor */
157
public static final ValueExtractorDescriptor DESCRIPTOR;
158
}
159
160
/**
161
* Value extractor specifically for NonEmptyStringParam
162
* Provides specialized validation support for non-empty string parameters
163
*/
164
public class NonEmptyStringParamValueExtractor implements ValueExtractor<NonEmptyStringParam> {
165
166
/** Extracts string value for validation */
167
public void extractValues(NonEmptyStringParam originalValue, ValueReceiver receiver);
168
169
/** Descriptor for registering the extractor */
170
public static final ValueExtractorDescriptor DESCRIPTOR;
171
}
172
```
173
174
### Jersey Parameter Name Provider
175
176
Parameter name provider that integrates with Jersey to provide meaningful parameter names for validation error messages.
177
178
```java { .api }
179
/**
180
* Parameter name provider for Jersey integration
181
* Provides parameter names from JAX-RS annotations for validation errors
182
*/
183
public class JerseyParameterNameProvider implements ParameterNameProvider {
184
185
/** Gets parameter names for method parameters using JAX-RS annotations */
186
public List<String> getParameterNames(Method method);
187
188
/** Gets parameter names for constructor parameters */
189
public List<String> getParameterNames(Constructor<?> constructor);
190
}
191
```
192
193
### Validation Configuration
194
195
```java { .api }
196
/**
197
* Hibernate Validation binder for Jersey integration
198
* Registers validation components with Jersey's dependency injection
199
*/
200
public class HibernateValidationBinder extends AbstractBinder {
201
202
/** Configures validation bindings */
203
protected void configure();
204
}
205
206
/**
207
* Mutable validator factory for constraint validators
208
* Allows dynamic constraint validator registration
209
*/
210
public class MutableValidatorFactory implements ConstraintValidatorFactory {
211
212
/** Creates or retrieves constraint validator instance */
213
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key);
214
215
/** Releases constraint validator instance */
216
public void releaseInstance(ConstraintValidator<?, ?> instance);
217
}
218
219
/**
220
* Dropwizard-configured validator context resolver
221
* Provides configured validators for Jersey resources
222
*/
223
public class DropwizardConfiguredValidator implements ContextResolver<Validator> {
224
225
/** Gets configured validator instance */
226
public Validator getContext(Class<?> type);
227
}
228
```
229
230
### Fuzzy Enum Validation
231
232
Parameter converter that provides fuzzy matching for enum values with validation support.
233
234
```java { .api }
235
/**
236
* Parameter converter for enum types with fuzzy matching
237
* Provides case-insensitive and partial matching for enum parameters
238
* @param <T> the enum type
239
*/
240
public class FuzzyEnumParamConverter<T extends Enum<T>> implements ParamConverter<T> {
241
242
/** Converts string to enum with fuzzy matching */
243
public T fromString(String value);
244
245
/** Converts enum to string representation */
246
public String toString(T value);
247
248
/** Gets enum type being converted */
249
public Class<T> getEnumType();
250
}
251
252
/**
253
* Provider for fuzzy enum parameter converters
254
* Automatically provides converters for enum types
255
*/
256
public class FuzzyEnumParamConverterProvider implements ParamConverterProvider {
257
258
/** Gets parameter converter for enum types */
259
public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations);
260
}
261
```
262
263
## Validation Usage Patterns
264
265
### Resource Method Validation
266
267
```java
268
import io.dropwizard.jersey.params.UUIDParam;
269
import jakarta.validation.Valid;
270
import jakarta.validation.constraints.*;
271
import jakarta.ws.rs.*;
272
273
@Path("/users")
274
public class UserResource {
275
276
@POST
277
@Consumes(MediaType.APPLICATION_JSON)
278
public Response createUser(@Valid @NotNull CreateUserRequest request) {
279
// @Valid triggers validation of the request object
280
// @NotNull ensures request is not null
281
User user = userService.create(request);
282
return Response.status(201).entity(user).build();
283
}
284
285
@PUT
286
@Path("/{id}")
287
public Response updateUser(@PathParam("id") UUIDParam userId,
288
@Valid UpdateUserRequest request) {
289
// Both parameter and request body validation
290
User user = userService.update(userId.get(), request);
291
return Response.ok(user).build();
292
}
293
294
@GET
295
public List<User> getUsers(@QueryParam("page") @Min(1) @Max(1000) Integer page,
296
@QueryParam("size") @Min(1) @Max(100) Integer size,
297
@QueryParam("status") UserStatus status) {
298
// Parameter-level validation constraints
299
int actualPage = page != null ? page : 1;
300
int actualSize = size != null ? size : 10;
301
return userService.getUsers(actualPage, actualSize, status);
302
}
303
}
304
```
305
306
### Request Object Validation
307
308
```java
309
import jakarta.validation.constraints.*;
310
import jakarta.validation.Valid;
311
import java.time.LocalDate;
312
import java.util.List;
313
314
public class CreateUserRequest {
315
316
@NotBlank(message = "Name is required")
317
@Size(min = 2, max = 50, message = "Name must be between 2 and 50 characters")
318
private String name;
319
320
@NotBlank(message = "Email is required")
321
@Email(message = "Email must be a valid email address")
322
private String email;
323
324
@Min(value = 18, message = "Age must be at least 18")
325
@Max(value = 120, message = "Age must be less than 120")
326
private Integer age;
327
328
@Past(message = "Birth date must be in the past")
329
private LocalDate birthDate;
330
331
@Valid // Cascade validation to nested objects
332
@NotNull(message = "Address is required")
333
private Address address;
334
335
@Size(max = 5, message = "Maximum 5 phone numbers allowed")
336
private List<@Pattern(regexp = "\\d{10}", message = "Phone number must be 10 digits") String> phoneNumbers;
337
338
// getters and setters
339
}
340
341
public class Address {
342
343
@NotBlank(message = "Street is required")
344
private String street;
345
346
@NotBlank(message = "City is required")
347
private String city;
348
349
@Pattern(regexp = "\\d{5}", message = "ZIP code must be 5 digits")
350
private String zipCode;
351
352
// getters and setters
353
}
354
```
355
356
### Custom Validation Constraints
357
358
```java
359
import jakarta.validation.Constraint;
360
import jakarta.validation.ConstraintValidator;
361
import jakarta.validation.ConstraintValidatorContext;
362
import jakarta.validation.Payload;
363
import java.lang.annotation.*;
364
365
@Target({ElementType.FIELD, ElementType.PARAMETER})
366
@Retention(RetentionPolicy.RUNTIME)
367
@Constraint(validatedBy = ValidUserId.Validator.class)
368
@Documented
369
public @interface ValidUserId {
370
371
String message() default "Invalid user ID format";
372
Class<?>[] groups() default {};
373
Class<? extends Payload>[] payload() default {};
374
375
class Validator implements ConstraintValidator<ValidUserId, String> {
376
377
@Override
378
public boolean isValid(String value, ConstraintValidatorContext context) {
379
if (value == null) return true; // Let @NotNull handle null checks
380
381
// Custom validation logic
382
return value.matches("^[A-Z]{2}\\d{6}$");
383
}
384
}
385
}
386
387
// Usage
388
public class UserRequest {
389
@ValidUserId
390
@NotNull
391
private String userId;
392
}
393
```
394
395
### Group Validation
396
397
```java
398
import jakarta.validation.groups.Default;
399
400
public interface CreateGroup {}
401
public interface UpdateGroup {}
402
403
public class UserRequest {
404
405
@NotNull(groups = {CreateGroup.class, UpdateGroup.class})
406
private String name;
407
408
@NotNull(groups = CreateGroup.class)
409
@Null(groups = UpdateGroup.class, message = "ID cannot be specified for updates")
410
private String id;
411
412
@Email(groups = {CreateGroup.class, UpdateGroup.class})
413
private String email;
414
}
415
416
@Path("/users")
417
public class UserResource {
418
419
@POST
420
public Response createUser(@Valid(CreateGroup.class) UserRequest request) {
421
// Validates with CreateGroup constraints
422
return Response.ok().build();
423
}
424
425
@PUT
426
@Path("/{id}")
427
public Response updateUser(@PathParam("id") String id,
428
@Valid(UpdateGroup.class) UserRequest request) {
429
// Validates with UpdateGroup constraints
430
return Response.ok().build();
431
}
432
}
433
```
434
435
## Validation Error Handling
436
437
### Error Response Format
438
439
Validation errors are returned as HTTP 422 responses with detailed field information:
440
441
```json
442
{
443
"code": 422,
444
"message": "Validation failed",
445
"errors": [
446
"name: Name is required",
447
"email: Email must be a valid email address",
448
"age: Age must be at least 18"
449
],
450
"fieldErrors": {
451
"name": ["Name is required"],
452
"email": ["Email must be a valid email address"],
453
"age": ["Age must be at least 18"]
454
}
455
}
456
```
457
458
### Custom Validation Error Handler
459
460
```java
461
@Provider
462
public class CustomValidationExceptionMapper implements ExceptionMapper<JerseyViolationException> {
463
464
@Override
465
public Response toResponse(JerseyViolationException exception) {
466
Map<String, List<String>> fieldErrors = new HashMap<>();
467
List<String> globalErrors = new ArrayList<>();
468
469
for (ConstraintViolation<?> violation : exception.getConstraintViolations()) {
470
String propertyPath = violation.getPropertyPath().toString();
471
String message = violation.getMessage();
472
473
if (propertyPath.isEmpty()) {
474
globalErrors.add(message);
475
} else {
476
fieldErrors.computeIfAbsent(propertyPath, k -> new ArrayList<>()).add(message);
477
}
478
}
479
480
ValidationErrorResponse error = new ValidationErrorResponse();
481
error.setMessage("Validation failed");
482
error.setFieldErrors(fieldErrors);
483
error.setGlobalErrors(globalErrors);
484
485
return Response.status(422).entity(error).build();
486
}
487
}
488
```
489
490
## Best Practices
491
492
### Validation Strategy
493
494
```java
495
public class ValidationBestPractices {
496
497
// Use appropriate validation annotations
498
@NotNull // For null checks
499
@NotBlank // For strings that should not be null, empty, or whitespace
500
@NotEmpty // For collections/arrays that should not be null or empty
501
502
@Size(min = 1, max = 100) // For length constraints
503
@Min(1) @Max(1000) // For numeric ranges
504
505
@Email // For email validation
506
@Pattern // For custom regex validation
507
@Past // For dates in the past
508
@Future // For dates in the future
509
510
// Cascade validation to nested objects
511
@Valid
512
private Address address;
513
514
// Validate collection elements
515
private List<@Valid ContactInfo> contacts;
516
517
// Group validation for different scenarios
518
@NotNull(groups = CreateGroup.class)
519
@Null(groups = UpdateGroup.class)
520
private String id;
521
}
522
```
523
524
### Parameter Validation
525
526
```java
527
@Path("/api")
528
public class ValidationExamples {
529
530
// Validate path parameters
531
@GET
532
@Path("/users/{id}")
533
public User getUser(@PathParam("id") @Pattern(regexp = "\\d+") String id) {
534
return userService.findById(Long.parseLong(id));
535
}
536
537
// Validate query parameters with defaults
538
@GET
539
@Path("/search")
540
public SearchResults search(
541
@QueryParam("q") @NotBlank @Size(min = 2, max = 100) String query,
542
@QueryParam("page") @Min(1) @DefaultValue("1") int page,
543
@QueryParam("size") @Min(1) @Max(100) @DefaultValue("10") int size) {
544
545
return searchService.search(query, page, size);
546
}
547
548
// Validate header parameters
549
@POST
550
@Path("/data")
551
public Response uploadData(
552
@HeaderParam("Content-Type") @Pattern(regexp = "application/.*") String contentType,
553
@Valid UploadRequest request) {
554
555
return Response.ok().build();
556
}
557
}
558
```