Validation is about checking syntax and semantics of input data. Invalid data is rejected by the application. Therefore validation is required in multiple places of an application. E.g. the GUI will do validation for usability reasons to assist the user, early feedback and to prevent unnecessary server requests. On the server-side validation has to be done for consistency and security.
In general we distinguish these forms of validation:
-
stateless validation will produce the same result for given input at any time (for the same code/release).
-
stateful validation is dependent on other states and can consider the same input data as valid in once case and as invalid in another.
For regular, stateless validation we use the JSR303 standard that is also called bean validation (BV). Details can be found in the specification. As implementation we recommend hibernate-validator.
A description of how to enable BV can be found in the relevant Spring documentation. For a quick summary follow these steps:
-
Make sure that hibernate-validator is located in the classpath by adding a dependency to the pom.xml.
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> </dependency>
-
Add the @Validated annotation to the implementation (spring bean) to be validated. The standard use case is to annotate the logic layer implementation, i.e. the use case implementation or component facade in case of simple logic layer pattern. Thus, the validation will be executed for service requests as well as batch processing. For methods to validate go to their declaration and add constraint annotations to the method parameters.
-
@Valid annotation to the arguments to validate (if that class itself is annotated with constraints to check).
-
@NotNull for required arguments.
-
Other constraints (e.g. @Size) for generic arguments (e.g. of type String or Integer). However, consider to create custom datatypes and avoid adding too much validation logic (especially redundant in multiple places). .BookingmanagementRestServiceImpl.java
-
@Validated public class BookingmanagementRestServiceImpl implements BookingmanagementRestService { ... public BookingEto saveBooking(@Valid BookingCto booking) { ...
-
Finally add appropriate validation constraint annotations to the fields of the ETO class. .BookingCto.java
@Valid private BookingEto booking;
@NotNull
@Future
private Timestamp bookingDate;
A list with all bean validation constraint annotations available for hibernate-validator can be found here. In addition it is possible to configure custom constraints. Therefor it is neccessary to implement a annotation and a corresponding validator. A description can also be found in the Spring documentation or with more details in the hibernate documentation.
Note
|
Bean Validation in Wildfly >v8: Wildfly v8 is the first version of Wildfly implementing the JEE7 specification. It comes with bean validation based on hibernate-validator out of the box. In case someone is running Spring in Wildfly for whatever reasons, the spring based annotation @Validated would duplicate bean validation at runtime and thus should be omitted. |
BV has poor support for this. Best practice is to create and use beans for ranges, etc. that solve this. A bean for a range could look like so:
public class Range<V extends Comparable<V>> {
private V min;
private V max;
public Range(V min, V max) {
super();
if ((min != null) && (max != null)) {
int delta = min.compareTo(max);
if (delta > 0) {
throw new ValueOutOfRangeException(null, min, min, max);
}
}
this.min = min;
this.max = max;
}
public V getMin() ...
public V getMax() ...
For complex and stateful business validations we do not use BV (possible with groups and context, etc.) but follow KISS and just implement this on the server in a straight forward manner. An example is the deletion of a table in the example application. Here the state of the table must be checked first: BookingmanagementImpl.java
private void sendConfirmationEmails(BookingEntity booking) {
if (!booking.getInvitedGuests().isEmpty()) {
for (InvitedGuestEntity guest : booking.getInvitedGuests()) {
sendInviteEmailToGuest(guest, booking);
}
}
sendConfirmationEmailToHost(booking);
}
Implementing this small check with BV would be a lot more effort.