In this fictitious scenario, I have an application that allows a user to search for arbitrary events based upon date; the relevant section of the my input form will look like this:
This is a purposely clumsy input form, but it serves our purpose as it gives us lots of fields to validate.
There are four steps required to enable validation:
- Add constraint annotations to your form’s command object.
- Add an annotation to the handler method in your controller that deals with this form.
- Modify your controller’s handler method, so that it re-displays your input form on validation failure.
- Add some error processing tags to your JSP
public class SearchCriteria extends FrameworkBean {
@NotEmpty(message = "Event type may not be null")
@Size(min = 4, max = 4, message = "EventType must be length four")
private String eventType;
private String description;
@Min(value = 1, message = "Start day must be greater than 0")
@Max(value = 31, message = "Start day must be less than 32")
private int startDay;
@Min(value = 1, message = "Start month must be greater than 0")
@Max(value = 12, message = "Start month must be less than 12")
private int startMonth;
@Min(value = 1970, message = "Start year must be greater than 1970")
private int startYear;
@Min(value = 1, message = "End day must be greater than 0")
@Max(value = 31, message = "End day must be less than 32")
private int endDay;
@Min(value = 1, message = "End month must be greater than 0")
@Max(value = 12, message = "End month must be less than 12")
private int endMonth;
@Min(value = 1971, message = "End year must be greater than 1971")
private int endYear;
In the above code snippet, you can see that I’m applying the @NotEmpty and @Size constraints to the eventType attribute and the @Min and @Max constraints to the date component fields. Notice that in applying these constraints, I’ve added an optional message attribute to my annotations. This is the message that we’ll be displaying on the screen if validation fails.
The second step is to add an @Valid annotation to your controller’s handler method:
@RequestMapping(value = SEARCH_PATH, method = RequestMethod.POST)
public String doSearch(@Valid SearchCriteria criteria, BindingResult bindingResult,
Model model) { etc...
This lets Spring know that the criteria argument of the doSearch() method needs validating.
The next important point to remember is that your controller method is called irrespective of whether or not your input form fails validation. This means that the way that you find out about validation errors is by checking a BindingResult object, which brings us to the next step: modifying your controller’s handler method, so that it re-displays your input form on validation failure. This really means adding an if statement to your handler method:
@RequestMapping(value = SEARCH_PATH, method = RequestMethod.POST)
public String doSearch(@Valid SearchCriteria criteria, BindingResult bindingResult,
Model model) {
logger.debug("Doing search with criteria: " + criteria);
String view;
if (!bindingResult.hasErrors()) {
List<ViewEvent> eventList = dummyDao.getDummyData(criteria);
updateModel(criteria, eventList, bindingResult, model);
view = getNextView(eventList);
} else {
view = FORM_VIEW;
}
return view;
}
The controller handler method above directs the user back to the search criteria input form if the binding result contains errors. The big idea here is that we design our form so that it’ll display error information when validation fails.
As you can see in the screen shot above, the error text in the red ‘failure’ area of the screen has been taken from the message attribute of each JSR 303 annotation. In order to display that information, you can use Spring’s form:errors tag using some JSP code that looks something like:
<%-- The Spring:hasBindErrors and both divs are only here to do the red colouring --%> <spring:hasBindErrors name="searchCriteria"> <div class="portlet-body"> <div class="failure"> <p><form:errors path="eventType" /></p> <p><form:errors path="startDay" /></p> <p><form:errors path="startMonth" /></p> <p><form:errors path="startYear" /></p> <p><form:errors path="endDay" /></p> <p><form:errors path="endMonth" /></p> <p><form:errors path="endYear" /></p> </div> </div> </spring:hasBindErrors>
It does appear that the validation is magically done by Spring and the Hibernate Validator after your user presses the Submit button and before your controller method is called; it’s something that you should need to have very little contact with. This may or may not be to your liking, I guess that it all depends upon how reliable this mechanism is and how it lets you know when you've done something wrong.
This is a simple overview on how to apply JSP 303 Bean Validation to your Spring 3 MVC web-app. There are some other details to look into that will make our application better. More on that next time...
No comments:
Post a comment