0
# Method Validation
1
2
Annotations and utilities for validating method parameters and enabling bean predicate validation, supporting advanced validation scenarios beyond field-level constraints. This provides method-level validation capabilities for Dropwizard applications.
3
4
## Capabilities
5
6
### Validated Annotation
7
8
Specifies validation groups for method parameters and return values, extending beyond the limitations of standard `@Valid` annotation.
9
10
```java { .api }
11
@Target({PARAMETER, METHOD})
12
@Retention(RUNTIME)
13
public @interface Validated {
14
/**
15
* Specify one or more validation groups to apply to the validation.
16
*
17
* @return Validation groups
18
*/
19
Class<?>[] value() default {Default.class};
20
}
21
```
22
23
**Usage Examples:**
24
25
```java
26
import io.dropwizard.validation.Validated;
27
import javax.validation.groups.Default;
28
29
public interface CreateUser {}
30
public interface UpdateUser {}
31
32
@Path("/users")
33
public class UserResource {
34
35
@POST
36
@Path("/create")
37
public Response createUser(@Validated(CreateUser.class) UserData userData) {
38
// Method parameter validated with CreateUser group
39
return Response.ok().build();
40
}
41
42
@PUT
43
@Path("/{id}")
44
public Response updateUser(
45
@PathParam("id") Long id,
46
@Validated({UpdateUser.class, Default.class}) UserData userData
47
) {
48
// Method parameter validated with both UpdateUser and Default groups
49
return Response.ok().build();
50
}
51
52
@POST
53
@Path("/batch")
54
public Response createUsers(@Validated List<@Validated(CreateUser.class) UserData> users) {
55
// Both the list and individual elements are validated
56
return Response.ok().build();
57
}
58
}
59
```
60
61
### ValidationMethod Annotation
62
63
Validates bean predicate methods as returning true. Bean predicates must be of the form `isSomething` or they'll be silently ignored.
64
65
```java { .api }
66
@Target({TYPE, ANNOTATION_TYPE, METHOD})
67
@Retention(RUNTIME)
68
@Constraint(validatedBy = MethodValidator.class)
69
public @interface ValidationMethod {
70
/**
71
* The validation message for this constraint.
72
*
73
* @return the message
74
*/
75
String message() default "is not valid";
76
77
/**
78
* The groups the constraint belongs to.
79
*
80
* @return an array of classes representing the groups
81
*/
82
Class<?>[] groups() default {};
83
84
/**
85
* The payloads of this constraint.
86
*
87
* @return the array of payload classes
88
*/
89
Class<? extends Payload>[] payload() default {};
90
}
91
```
92
93
**Usage Examples:**
94
95
```java
96
import io.dropwizard.validation.ValidationMethod;
97
98
public class UserConfiguration {
99
private String username;
100
private String password;
101
private int age;
102
private String email;
103
104
@ValidationMethod(message = "Username and password cannot be the same")
105
public boolean isUsernameAndPasswordDifferent() {
106
return username == null || password == null || !username.equals(password);
107
}
108
109
@ValidationMethod(message = "User must be at least 18 years old")
110
public boolean isAgeValid() {
111
return age >= 18;
112
}
113
114
@ValidationMethod(message = "Email domain must be from approved list")
115
public boolean isEmailDomainApproved() {
116
if (email == null) return true;
117
118
String[] approvedDomains = {"company.com", "partner.org", "trusted.net"};
119
String domain = email.substring(email.lastIndexOf("@") + 1);
120
121
return Arrays.asList(approvedDomains).contains(domain);
122
}
123
124
// getters and setters...
125
}
126
```
127
128
## Advanced Usage
129
130
### Combining Method Validation with Field Validation
131
132
```java
133
import io.dropwizard.validation.ValidationMethod;
134
import javax.validation.constraints.NotNull;
135
import javax.validation.constraints.Size;
136
import javax.validation.constraints.Email;
137
138
public class RegistrationData {
139
@NotNull
140
@Size(min = 3, max = 50)
141
private String username;
142
143
@NotNull
144
@Size(min = 8)
145
private String password;
146
147
@NotNull
148
149
private String email;
150
151
private String confirmPassword;
152
private boolean termsAccepted;
153
154
// Method validation for business rules
155
@ValidationMethod(message = "Password and confirmation must match")
156
public boolean isPasswordConfirmed() {
157
return password != null && password.equals(confirmPassword);
158
}
159
160
@ValidationMethod(message = "Terms and conditions must be accepted")
161
public boolean isTermsAccepted() {
162
return termsAccepted;
163
}
164
165
@ValidationMethod(message = "Username cannot be the same as email prefix")
166
public boolean isUsernameUniqueFromEmail() {
167
if (username == null || email == null) return true;
168
169
String emailPrefix = email.substring(0, email.indexOf("@"));
170
return !username.equalsIgnoreCase(emailPrefix);
171
}
172
}
173
```
174
175
### Method Validation with Groups
176
177
```java
178
import io.dropwizard.validation.ValidationMethod;
179
180
public interface BasicValidation {}
181
public interface AdvancedValidation {}
182
183
public class SecurityConfiguration {
184
private int passwordMinLength;
185
private int maxLoginAttempts;
186
private boolean requireTwoFactor;
187
188
@ValidationMethod(
189
message = "Password minimum length must be at least 8 characters",
190
groups = BasicValidation.class
191
)
192
public boolean isPasswordMinLengthSufficient() {
193
return passwordMinLength >= 8;
194
}
195
196
@ValidationMethod(
197
message = "Max login attempts must be reasonable (3-10)",
198
groups = BasicValidation.class
199
)
200
public boolean isMaxLoginAttemptsReasonable() {
201
return maxLoginAttempts >= 3 && maxLoginAttempts <= 10;
202
}
203
204
@ValidationMethod(
205
message = "Two-factor authentication should be enabled for password lengths less than 12",
206
groups = AdvancedValidation.class
207
)
208
public boolean isTwoFactorAppropriate() {
209
return passwordMinLength >= 12 || requireTwoFactor;
210
}
211
}
212
213
// Validate with specific groups
214
Validator validator = BaseValidator.newValidator();
215
Set<ConstraintViolation<SecurityConfiguration>> basicViolations =
216
validator.validate(config, BasicValidation.class);
217
Set<ConstraintViolation<SecurityConfiguration>> advancedViolations =
218
validator.validate(config, AdvancedValidation.class);
219
```
220
221
### JAX-RS Resource Method Validation
222
223
```java
224
import io.dropwizard.validation.Validated;
225
import javax.validation.Valid;
226
import javax.validation.constraints.NotNull;
227
228
@Path("/api/orders")
229
public class OrderResource {
230
231
@POST
232
@Path("/create")
233
public Response createOrder(
234
@Valid @NotNull OrderRequest request,
235
@Validated(CreateOrder.class) @NotNull OrderMetadata metadata
236
) {
237
// Both request and metadata are validated
238
// request uses standard validation, metadata uses CreateOrder group
239
return Response.ok().build();
240
}
241
242
@PUT
243
@Path("/{id}/update")
244
public Response updateOrder(
245
@PathParam("id") @NotNull Long orderId,
246
@Validated({UpdateOrder.class, Default.class}) OrderRequest request
247
) {
248
// request validated with both UpdateOrder and Default groups
249
return Response.ok().build();
250
}
251
252
@GET
253
@Path("/search")
254
public Response searchOrders(
255
@Validated(SearchCriteria.class) @BeanParam OrderSearchParams params
256
) {
257
// Query parameters validated with SearchCriteria group
258
return Response.ok().build();
259
}
260
}
261
262
// Validation groups
263
public interface CreateOrder {}
264
public interface UpdateOrder {}
265
public interface SearchCriteria {}
266
```
267
268
### Complex Business Rule Validation
269
270
```java
271
import io.dropwizard.validation.ValidationMethod;
272
273
public class FinancialConfiguration {
274
private BigDecimal interestRate;
275
private int loanTermMonths;
276
private BigDecimal maxLoanAmount;
277
private BigDecimal minDownPayment;
278
private String creditScoreRequirement;
279
280
@ValidationMethod(message = "Interest rate must be positive and reasonable (0.1% to 50%)")
281
public boolean isInterestRateValid() {
282
if (interestRate == null) return true;
283
284
BigDecimal minRate = new BigDecimal("0.001"); // 0.1%
285
BigDecimal maxRate = new BigDecimal("0.50"); // 50%
286
287
return interestRate.compareTo(minRate) >= 0 &&
288
interestRate.compareTo(maxRate) <= 0;
289
}
290
291
@ValidationMethod(message = "Loan term must be between 6 months and 30 years")
292
public boolean isLoanTermReasonable() {
293
return loanTermMonths >= 6 && loanTermMonths <= 360;
294
}
295
296
@ValidationMethod(message = "Down payment cannot exceed maximum loan amount")
297
public boolean isDownPaymentAppropriate() {
298
if (minDownPayment == null || maxLoanAmount == null) return true;
299
300
return minDownPayment.compareTo(maxLoanAmount) <= 0;
301
}
302
303
@ValidationMethod(message = "Higher interest rates require lower credit score requirements")
304
public boolean isCreditScoreConsistentWithRate() {
305
if (interestRate == null || creditScoreRequirement == null) return true;
306
307
// Business logic: higher rates should allow lower credit scores
308
BigDecimal highRateThreshold = new BigDecimal("0.10"); // 10%
309
310
if (interestRate.compareTo(highRateThreshold) > 0) {
311
// High rate should allow lower credit requirements
312
return !creditScoreRequirement.equals("EXCELLENT");
313
}
314
315
return true;
316
}
317
}
318
```
319
320
## Integration Notes
321
322
### JAX-RS Integration
323
324
- `@Validated` works with JAX-RS resource methods for parameter validation
325
- Integrates with Dropwizard's Jersey validation to provide detailed error responses
326
- Can be combined with `@Valid` for comprehensive validation coverage
327
- Supports validation of `@BeanParam`, `@QueryParam`, `@PathParam`, and request body parameters
328
329
### Bean Validation Integration
330
331
- `@ValidationMethod` integrates seamlessly with standard Bean Validation annotations
332
- Method validation occurs in addition to field-level validation
333
- Supports validation groups for conditional validation scenarios
334
- Works with inheritance - validation methods in parent classes are also executed
335
336
### Best Practices
337
338
- Use `@Validated` when you need specific validation groups or ordered validation
339
- Use `@ValidationMethod` for cross-field validation and complex business rules
340
- Method names for `@ValidationMethod` should follow the `isSomething()` pattern
341
- Combine method validation with field validation for comprehensive coverage
342
- Consider performance implications of complex validation methods in high-throughput scenarios
343
344
### Error Handling
345
346
- Method validation violations are reported alongside field validation violations
347
- Custom error messages are supported and recommended for business rule clarity
348
- Violations can be processed using `ConstraintViolations.format()` utility methods