Form Validation
Set up
Add Hibernate's
library (Hibernate Validator)for Bean Validation
which is fully compliant with Java's
Bean Validation API
.
- Required Validation
- Number Range Validation
- Validation with Regular Expressions
- Handle String in Integer Field
- Custom Validation
Required Validation
Add Validation Rule to Bean
We create a Customer
class, whose lastName
attribute must be non-null, that is, lastName
is a required attribute:
package com.springdemo.mvc;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class Customer {
private String firstName;
// Validation annotation
@NotNull(message="is required")
@Size(min=1, message="is required")
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Note that if we wanted to make an integer required, we must use the wrapper java classes (i.e. Integer), that will be able to handle empty strings as inputs and nulls. The primitive types will throw an exception.
Perform Validation in the Controller
We also
package com.springdemo.mvc;
import javax.validation.Valid;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/customer")
public class CustomerController {
// add an initbinder ... to convert trim input strings
// remove leading and trailing whitespace
// resolve issue for our validation
@InitBinder
//@InitBinder annotation works as a pre-processor
// It will pre-process each web request to our controller
public void initBinder(WebDataBinder dataBinder) {
// Trim strings (true: empty strings to null)
StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true);
// For every string class apply the trim editor
dataBinder.registerCustomEditor(String.class, stringTrimmerEditor);
}
@RequestMapping("/showForm")
public String showForm(Model theModel) {
theModel.addAttribute("customer", new Customer());
return "customer-form";
}
@RequestMapping("/processForm")
// @Valid: Tells spring to perform validation on the customer object
// BindingResult: results of the validation will be placed in BindingResult
public String processForm(
@Valid @ModelAttribute("customer") Customer theCustomer,
BindingResult theBindingResult) {
System.out.println("Last name: |" + theCustomer.getLastName() + "|");
// Check if validation was sucessfull
if (theBindingResult.hasErrors()) {
// If not sucessfull send back
return "customer-form";
}
else {
// If sucessfull
return "customer-confirmation";
}
}
}
When performing Spring MVC validation, the location of the BindingResult parameter is very important. In the method signature, the BindingResult parameter must appear immediately after the model attribute.
Display error on HTML
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<title>Customer Registration Form</title>
<style>
.error {color:red}
</style>
</head>
<body>
<i>Fill out the form. Asterisk (*) means required.</i>
<br><br>
<form:form action="processForm" modelAttribute="customer">
First name: <form:input path="firstName" />
<br><br>
Last name (*): <form:input path="lastName" />
<!-- We use the error form tag to display an error when the input is not valid -->
<!-- The message shown equals the messages from both of the validation annotations defined
for the lastName attribute in the Customer class -->
<form:errors path="lastName" cssClass="error" />
<br><br>
<input type="submit" value="Submit" />
</form:form>
</body>
</html>
Number Range Validation
Add Validation Rule to Bean
We create a Customer
class, whose freePasses
variable must be a number between 0 and 10.
public class Customer {
private String firstName;
@NotNull(message="is required")
@Size(min=1, message="is required")
private String lastName;
// Minimum value we will expect
@Min(value=0, message="must be greater than or equal to zero")
// Maximum value we will expect
@Max(value=10, message="must be less than or equal to 10")
private int freePasses;
...
Perform Validation in the Controller
We also
package com.springdemo.mvc;
import javax.validation.Valid;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/customer")
public class CustomerController {
// add an initbinder ... to convert trim input strings
// remove leading and trailing whitespace
// resolve issue for our validation
@InitBinder
//@InitBinder annotation works as a pre-processor
// It will pre-process each web request to our controller
public void initBinder(WebDataBinder dataBinder) {
// Trim strings (true: empty strings to null)
StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true);
// For every string class apply the trim editor
dataBinder.registerCustomEditor(String.class, stringTrimmerEditor);
}
@RequestMapping("/showForm")
public String showForm(Model theModel) {
theModel.addAttribute("customer", new Customer());
return "customer-form";
}
@RequestMapping("/processForm")
// @Valid: Tells spring to perform validation on the customer object
// BindingResult: results of the validation will be placed in BindingResult
public String processForm(
@Valid @ModelAttribute("customer") Customer theCustomer,
BindingResult theBindingResult) {
System.out.println("Last name: |" + theCustomer.getLastName() + "|");
// Check if validation was sucessfull
if (theBindingResult.hasErrors()) {
// If not sucessfull send back
return "customer-form";
}
else {
// If sucessfull
return "customer-confirmation";
}
}
}
When performing Spring MVC validation, the location of the BindingResult parameter is very important. In the method signature, the BindingResult parameter must appear immediately after the model attribute.
Display error on HTML
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<title>Customer Registration Form</title>
<style>
.error {color:red}
</style>
</head>
<body>
<i>Fill out the form. Asterisk (*) means required.</i>
<br><br>
<form:form action="processForm" modelAttribute="customer">
First name: <form:input path="firstName" />
<br><br>
Last name (*): <form:input path="lastName" />
<!-- The message shown equals the messages from both of the validation annotations defined
for the lastName attribute in the Customer class -->
<form:errors path="lastName" cssClass="error" />
<br><br>
Free passes: <form:input path="freePasses" />
<!-- The message shown equals the messages from both of the validation annotations defined
for the freePasses attribute in the Customer class -->
<form:errors path="freePasses" cssClass="error" />
<br><br>
<input type="submit" value="Submit" />
</form:form>
</body>
</html>
Validation with Regular Expressions
Add Validation Rule to Bean
We create a Customer
class, whose freePasses
variable must be a number between 0 and 10.
public class Customer {
private String firstName;
@NotNull(message="is required")
@Size(min=1, message="is required")
private String lastName;
@Min(value=0, message="must be greater than or equal to zero")
@Max(value=10, message="must be less than or equal to 10")
private int freePasses;
// Define the regular expression for the postalCode attribute
@Pattern(regexp="^[a-zA-Z0-9]{5}", message="only 5 chars/digits")
private String postalCode;
...
Perform Validation in the Controller
We also
package com.springdemo.mvc;
import javax.validation.Valid;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/customer")
public class CustomerController {
// add an initbinder ... to convert trim input strings
// remove leading and trailing whitespace
// resolve issue for our validation
@InitBinder
//@InitBinder annotation works as a pre-processor
// It will pre-process each web request to our controller
public void initBinder(WebDataBinder dataBinder) {
// Trim strings (true: empty strings to null)
StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true);
// For every string class apply the trim editor
dataBinder.registerCustomEditor(String.class, stringTrimmerEditor);
}
@RequestMapping("/showForm")
public String showForm(Model theModel) {
theModel.addAttribute("customer", new Customer());
return "customer-form";
}
@RequestMapping("/processForm")
// @Valid: Tells spring to perform validation on the customer object
// BindingResult: results of the validation will be placed in BindingResult
public String processForm(
@Valid @ModelAttribute("customer") Customer theCustomer,
BindingResult theBindingResult) {
System.out.println("Last name: |" + theCustomer.getLastName() + "|");
// Check if validation was sucessfull
if (theBindingResult.hasErrors()) {
// If not sucessfull send back
return "customer-form";
}
else {
// If sucessfull
return "customer-confirmation";
}
}
}
When performing Spring MVC validation, the location of the BindingResult parameter is very important. In the method signature, the BindingResult parameter must appear immediately after the model attribute.
Display error on HTML
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<title>Customer Registration Form</title>
<style>
.error {color:red}
</style>
</head>
<body>
<i>Fill out the form. Asterisk (*) means required.</i>
<br><br>
<form:form action="processForm" modelAttribute="customer">
First name: <form:input path="firstName" />
<br><br>
Last name (*): <form:input path="lastName" />
<form:errors path="lastName" cssClass="error" />
<br><br>
Free passes: <form:input path="freePasses" />
<form:errors path="freePasses" cssClass="error" />
<br><br>
Postal Code: <form:input path="postalCode" />
<!-- The message shown equals the messages from both of the validation annotations defined
for the postalCode attribute in the Customer class -->
<form:errors path="postalCode" cssClass="error" />
<br><br>
<input type="submit" value="Submit" />
</form:form>
</body>
</html>
Handle String Input in Integer Field
Create a custom message
Create a properties file in resources/messages.properties
// ErrorType.SpringModelAttributeName.FieldName
typeMismatch.customer.freePasses=Invalid number
Specify Properties file in Configuration
We add the following in our configuration file spring-mvc-demo-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.luv2code.springdemo" />
<mvc:annotation-driven/>
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- Load custom message resources -->
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<!-- Path where the properties file is stored -->
<property name="basenames" value="resources/messages" />
</bean>
</beans>
Custom Validation
Create a Custom Java Annotation
Create Annotation Clas
package com.springdemo.mvc.validation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
// Specify the class that holds the validation logic
@Constraint(validatedBy = CourseCodeConstraintValidator.class)
// Where you can use this annotation: on a method or on a field
@Target( { ElementType.METHOD, ElementType.FIELD } )
@Retention(RetentionPolicy.RUNTIME)
// Note the @interface (it is needed to create the annotation)
public @interface CourseCode {
// define default course code
public String value() default "LUV";
// define default error message
public String message() default "must start with LUV";
// define default groups
public Class<?>[] groups() default {};
// define default payloads
public Class<? extends Payload>[] payload() default {};
}
Create Validator Class
This class holds the validation logic
package com.springdemo.mvc.validation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
// Implements the previous ConstraintValidator interface, with generics: <Annotation Interface, Data Type>
public class CourseCodeConstraintValidator
implements ConstraintValidator<CourseCode, String> {
private String coursePrefix;
@Override
public void initialize(CourseCode theCourseCode) {
// Obtain prefix from the "value" attribute of our annotation
coursePrefix = theCourseCode.value();
}
@Override
// Called when we use the @Valid annotation
public boolean isValid(String theCode, ConstraintValidatorContext theConstraintValidatorContext) {
boolean result;
// Validation logic
if (theCode != null) {
result = theCode.startsWith(coursePrefix);
}
else {
result = true;
}
return result;
}
}
Add Custom Validation
public class Customer {
private String firstName;
@NotNull(message="is required")
@Size(min=1, message="is required")
private String lastName;
@NotNull(message="is required")
@Min(value=0, message="must be greater than or equal to zero")
@Max(value=10, message="must be less than or equal to 10")
private Integer freePasses;
@Pattern(regexp="^[a-zA-Z0-9]{5}", message="only 5 chars/digits")
private String postalCode;
// Use our custom validation tag
@CourseCode(value="TOPS", message="must start with TOPS")
private String courseCode;
Perform Validation on Controller
package com.springdemo.mvc;
import javax.validation.Valid;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/customer")
public class CustomerController {
// add an initbinder ... to convert trim input strings
// remove leading and trailing whitespace
// resolve issue for our validation
@InitBinder
//@InitBinder annotation works as a pre-processor
// It will pre-process each web request to our controller
public void initBinder(WebDataBinder dataBinder) {
// Trim strings (true: empty strings to null)
StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true);
// For every string class apply the trim editor
dataBinder.registerCustomEditor(String.class, stringTrimmerEditor);
}
@RequestMapping("/showForm")
public String showForm(Model theModel) {
theModel.addAttribute("customer", new Customer());
return "customer-form";
}
@RequestMapping("/processForm")
// @Valid: Tells spring to perform validation on the customer object
// BindingResult: results of the validation will be placed in BindingResult
public String processForm(
@Valid @ModelAttribute("customer") Customer theCustomer,
BindingResult theBindingResult) {
System.out.println("Last name: |" + theCustomer.getLastName() + "|");
// Check if validation was sucessfull
if (theBindingResult.hasErrors()) {
// If not sucessfull send back
return "customer-form";
}
else {
// If sucessfull
return "customer-confirmation";
}
}
}
When performing Spring MVC validation, the location of the BindingResult parameter is very important. In the method signature, the BindingResult parameter must appear immediately after the model attribute.
Display error on HTML
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<title>Customer Registration Form</title>
<style>
.error {color:red}
</style>
</head>
<body>
<i>Fill out the form. Asterisk (*) means required.</i>
<br><br>
<form:form action="processForm" modelAttribute="customer">
First name: <form:input path="firstName" />
<br><br>
Last name (*): <form:input path="lastName" />
<form:errors path="lastName" cssClass="error" />
<br><br>
Free passes: <form:input path="freePasses" />
<form:errors path="freePasses" cssClass="error" />
<br><br>
Postal Code: <form:input path="postalCode" />
<form:errors path="postalCode" cssClass="error" />
<br><br>
<!-- The message shown equals the messages from both of the validation annotations defined
for the courseCode attribute in the Customer class -->
Course Code: <form:input path="courseCode" />
<form:errors path="courseCode" cssClass="error" />
<br><br>
<input type="submit" value="Submit" />
</form:form>
</body>
</html>