Exemplo de Validação Spring – Validador de Formulário Spring MVC

Quando aceitamos entradas de usuários em qualquer aplicativo web, torna-se necessário validá-las. Podemos validar a entrada do usuário no lado do cliente usando JavaScript, mas também é necessário validá-las no lado do servidor para garantir que estejamos processando dados válidos no caso de o usuário ter o JavaScript desativado.

Validação do Spring

Spring MVC Framework oferece suporte às especificações JSR-303 por padrão, e tudo o que precisamos fazer é adicionar JSR-303 e suas dependências de implementação na aplicação Spring MVC. O Spring também fornece a anotação @Validator e a classe BindingResult, por meio das quais podemos obter os erros gerados pela implementação do validador no método de manipulação de solicitações do controlador. Podemos criar nossas implementações personalizadas de validadores de duas maneiras – a primeira é criar uma anotação que esteja em conformidade com as especificações JSR-303 e implementar sua classe Validator. A segunda abordagem é implementar a interface org.springframework.validation.Validator e defini-la como validador na classe do controlador usando a anotação @InitBinder. Vamos criar um projeto simples do Spring MVC no Spring Tool Suite, onde usaremos as especificações JSR-303 com seu artefato de implementação hibernate-validator. Vamos usar a validação de formulário baseada em anotações e criar nosso próprio validador personalizado com base nos padrões das especificações JSR-303. Também criaremos nossa própria classe de validador personalizado, implementando a interface Validator e a usaremos em um dos métodos de manipulação do controlador. Nosso projeto final se parece com a imagem abaixo. Vamos analisar cada um dos componentes individualmente.

Validador de Formulário Spring MVC

Nosso arquivo pom.xml final parece com o abaixo. Além dos artefatos padrão do Spring MVC, temos dependências de validation-api e hibernate-validator no projeto.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.journaldev</groupId>
	<artifactId>spring</artifactId>
	<name>SpringFormValidation</name>
	<packaging>war</packaging>
	<version>1.0.0-BUILD-SNAPSHOT</version>
	<properties>
		<java-version>1.7</java-version>
		<org.springframework-version>4.0.2.RELEASE</org.springframework-version>
		<org.aspectj-version>1.7.4</org.aspectj-version>
		<org.slf4j-version>1.7.5</org.slf4j-version>
	</properties>
	<dependencies>
	<!-- Form Validation using Annotations -->  
		<dependency>
			<groupId>javax.validation</groupId>
			<artifactId>validation-api</artifactId>
			<version>1.1.0.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-validator</artifactId>
			<version>4.1.0.Final</version>
		</dependency>
		
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
			<exclusions>
				<!-- Exclude Commons Logging in favor of SLF4j -->
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

		<!-- AspectJ -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>

		<!-- Logging -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${org.slf4j-version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.15</version>
			<exclusions>
				<exclusion>
					<groupId>javax.mail</groupId>
					<artifactId>mail</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.jms</groupId>
					<artifactId>jms</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jdmk</groupId>
					<artifactId>jmxtools</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jmx</groupId>
					<artifactId>jmxri</artifactId>
				</exclusion>
			</exclusions>
			<scope>runtime</scope>
		</dependency>

		<!-- @Inject -->
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>

		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- Test -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.7</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-eclipse-plugin</artifactId>
				<version>2.9</version>
				<configuration>
					<additionalProjectnatures>
						<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
					</additionalProjectnatures>
					<additionalBuildcommands>
						<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
					</additionalBuildcommands>
					<downloadSources>true</downloadSources>
					<downloadJavadocs>true</downloadJavadocs>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.5.1</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
					<compilerArgument>-Xlint:all</compilerArgument>
					<showWarnings>true</showWarnings>
					<showDeprecation>true</showDeprecation>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>exec-maven-plugin</artifactId>
				<version>1.2.1</version>
				<configuration>
					<mainClass>org.test.int1.Main</mainClass>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Descritor de Implantação

Ao criar um projeto Spring MVC a partir do STS, ele cria dois arquivos de configuração de contexto. Eu os limpei um pouco e tenho apenas um arquivo de configuração de bean Spring. Meu arquivo web.xml final parece com o abaixo.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="https://java.sun.com/xml/ns/javaee"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	
	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/spring.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
		
	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

</web-app>

Arquivo de Configuração de Bean Spring

Geralmente olhamos para as configurações de fiação do Spring por último, mas desta vez não temos muitas configurações no arquivo de configuração de bean Spring. Nosso arquivo spring.xml final parece com o abaixo.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="https://www.springframework.org/schema/mvc"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="https://www.springframework.org/schema/beans"
	xmlns:context="https://www.springframework.org/schema/context"
	xsi:schemaLocation="https://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<beans:bean id="employeeValidator" class="com.journaldev.spring.form.validator.EmployeeFormValidator" />
	
	<beans:bean id="messageSource"
		class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
		<beans:property name="basename" value="classpath:message" />
		<beans:property name="defaultEncoding" value="UTF-8" />
	</beans:bean>
	
	<context:component-scan base-package="com.journaldev.spring" />
	
</beans:beans>

O único ponto importante a ser observado são os beans employeeValidator que injetaremos em um dos controladores e messageSource para ler os dados localizados nos pacotes de recursos. O restante da parte é para suportar anotações, resolutores de visualização e fornecer pacote para escanear classes de controlador e outros componentes.

Classes de Modelo

Neste projeto, temos duas classes de modelo – a primeira onde usaremos a anotação JSR-303 e nosso validador baseado em anotações personalizadas e a segunda onde usaremos apenas nossa implementação de validador. Código Customer.java:

package com.journaldev.spring.form.model;

import java.util.Date;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;

import com.journaldev.spring.form.validator.Phone;

public class Customer {

	@Size(min=2, max=30) 
    private String name;
     
    @NotEmpty @Email
    private String email;
     
    @NotNull @Min(18) @Max(100)
    private Integer age;
     
    @NotNull
    private Gender gender;
     
    @DateTimeFormat(pattern="MM/dd/yyyy")
    @NotNull @Past
    private Date birthday;
    
    @Phone
    private String phone;
    
    public enum Gender {
		MALE, FEMALE
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public Gender getGender() {
		return gender;
	}

	public void setGender(Gender gender) {
		this.gender = gender;
	}

	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}
	
}

Observe que estamos usando as anotações @Email, @NotEmpty e @DateTimeFormat, que são adicionais ao JSR-303 e fornecidas pela implementação do validador do Hibernate. Algumas das anotações JSR-303 que estamos usando são @Size, @NotNull, etc. A anotação @Phone usada é nossa implementação personalizada com base nas especificações JSR-303, vamos analisá-la na próxima seção. Código Employee.java:

package com.journaldev.spring.form.model;

public class Employee {

	private int id;
	private String name;
	private String role;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getRole() {
		return role;
	}
	public void setRole(String role) {
		this.role = role;
	}
	
}

O Employee é um bean Java padrão e usaremos nossa implementação de validador personalizado para validar o formulário com o bean Employee.

Implementações de Validador Personalizadas

Código Phone.java:

package com.journaldev.spring.form.validator;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;

import javax.validation.Constraint;
import javax.validation.Payload;

@Documented
@Constraint(validatedBy = PhoneValidator.class)
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Phone {
 
     
    String message() default "{Phone}";
     
    Class<?>[] groups() default {};
     
    Class<? extends Payload>[] payload() default {};
      
}

A maior parte é código boilerplate para confirmar com as especificações JSR-303. A parte mais importante é a anotação @Constraint onde fornecemos a classe que será usada para validação, ou seja, PhoneValidator. Código PhoneValidator.java:

package com.journaldev.spring.form.validator;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class PhoneValidator implements ConstraintValidator {

	@Override
	public void initialize(Phone paramA) {
	}

	@Override
	public boolean isValid(String phoneNo, ConstraintValidatorContext ctx) {
		if(phoneNo == null){
			return false;
		}
		//validar números de telefone no formato "1234567890"
        if (phoneNo.matches("\\d{10}")) return true;
        //validar número de telefone com -, . ou espaços
        else if(phoneNo.matches("\\d{3}[-\\.\\s]\\d{3}[-\\.\\s]\\d{4}")) return true;
        //validar número de telefone com extensão de comprimento de 3 a 5
        else if(phoneNo.matches("\\d{3}-\\d{3}-\\d{4}\\s(x|(ext))\\d{3,5}")) return true;
        //validar número de telefone onde o código de área está entre parênteses ()
        else if(phoneNo.matches("\\(\\d{3}\\)-\\d{3}-\\d{4}")) return true;
        //retornar falso se nada corresponder à entrada
        else return false;
	}

}

Nossa implementação de validador de especificações JSR-303 deve implementar a interface javax.validation.ConstraintValidator. Se estivermos usando algum recurso como DataSource, podemos inicializá-lo no método initialize(). O método de validação é isValid e ele retorna verdadeiro se os dados forem válidos, caso contrário, deve retornar falso. Se você é novo em expressões regulares, pode ler mais sobre isso em Java Regular Expressions Tutorial. Código da classe EmployeeFormValidator.java:

package com.journaldev.spring.form.validator;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

import com.journaldev.spring.form.model.Employee;

public class EmployeeFormValidator implements Validator {

	//quais objetos podem ser validados por este validador
	@Override
	public boolean supports(Class paramClass) {
		return Employee.class.equals(paramClass);
	}

	@Override
	public void validate(Object obj, Errors errors) {
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id", "id.required");
		
		Employee emp = (Employee) obj;
		if(emp.getId() <=0){
			errors.rejectValue("id", "negativeValue", new Object[]{"'id'"}, "id can't be negative");
		}
		
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "name.required");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "role", "role.required");
	}
}

A classe EmployeeFormValidator é a implementação do validador específico para o Framework Spring. O método supports() é implementado pelo Framework Spring para saber em quais objetos essa validação pode ser usada. Nós implementamos o método validate() e adicionamos erros se alguma validação de campo falhar. O Spring fornece a classe utilitária org.springframework.validation.ValidationUtils para validações básicas, como nulo ou vazio. Uma vez que esse método retorna, o framework Spring vincula o objeto Errors ao objeto BindingResult que usamos em nosso método de manipulador do controlador. Observe que o último argumento de ValidationUtils.rejectIfEmptyOrWhitespace() recebe o nome da chave para recursos de mensagem. Dessa forma, podemos fornecer mensagens de erro localizadas para o usuário. Para obter mais informações sobre i18n no Spring, leia Exemplo de i18n no Spring.

Classes de Controlador

Temos duas classes de controlador, uma para validação de formulários baseada em anotações e outra para nosso validador personalizado. Código da classe CustomerController.java:

package com.journaldev.spring.form.controllers;

import java.util.HashMap;
import java.util.Map;

import javax.validation.Valid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.journaldev.spring.form.model.Customer;

@Controller
public class CustomerController {

	private static final Logger logger = LoggerFactory
			.getLogger(CustomerController.class);
	
	private Map<String, Customer> customers = null;
	
	public CustomerController(){
		customers = new HashMap<String, Customer>();
	}

	@RequestMapping(value = "/cust/save", method = RequestMethod.GET)
	public String saveCustomerPage(Model model) {
		logger.info("Returning custSave.jsp page");
		model.addAttribute("customer", new Customer());
		return "custSave";
	}

	@RequestMapping(value = "/cust/save.do", method = RequestMethod.POST)
	public String saveCustomerAction(
			@Valid Customer customer,
			BindingResult bindingResult, Model model) {
		if (bindingResult.hasErrors()) {
			logger.info("Returning custSave.jsp page");
			return "custSave";
		}
		logger.info("Returning custSaveSuccess.jsp page");
		model.addAttribute("customer", customer);
		customers.put(customer.getEmail(), customer);
		return "custSaveSuccess";
	}

}

Quando usamos validação de formulário baseada em anotações, só precisamos fazer pequenas alterações na implementação do método manipulador do nosso controlador para fazê-lo funcionar. Primeiro, precisamos anotar o objeto modelo que desejamos validar com a anotação `@Valid`. Em seguida, precisamos ter um argumento BindingResult no método, o Spring se encarrega de preenchê-lo com mensagens de erro. A lógica do método manipulador é muito simples: se houver erros, respondemos com a mesma página; caso contrário, redirecionamos o usuário para a página de sucesso. Outro ponto importante a ser observado é que estamos adicionando o atributo “customer” ao modelo, isso é necessário para permitir que o framework Spring saiba qual objeto modelo usar na página do formulário. Se não o fizermos, a vinculação do objeto aos dados do formulário não ocorrerá e nossa validação de formulário não funcionará. Código da classe EmployeeController.java:

package com.journaldev.spring.form.controllers;

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Validator;
import org.springframework.validation.annotation.Validated;
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;
import org.springframework.web.bind.annotation.RequestMethod;

import com.journaldev.spring.form.model.Employee;

@Controller
public class EmployeeController {

	private static final Logger logger = LoggerFactory
			.getLogger(EmployeeController.class);

	private Map emps = null;

	@Autowired
	@Qualifier("employeeValidator")
	private Validator validator;

	@InitBinder
	private void initBinder(WebDataBinder binder) {
		binder.setValidator(validator);
	}

	public EmployeeController() {
		emps = new HashMap();
	}

	@ModelAttribute("employee")
	public Employee createEmployeeModel() {
		// O valor do ModelAttribute deve ser o mesmo usado no empSave.jsp
		return new Employee();
	}

	@RequestMapping(value = "/emp/save", method = RequestMethod.GET)
	public String saveEmployeePage(Model model) {
		logger.info("Returning empSave.jsp page");
		return "empSave";
	}

	@RequestMapping(value = "/emp/save.do", method = RequestMethod.POST)
	public String saveEmployeeAction(
			@ModelAttribute("employee") @Validated Employee employee,
			BindingResult bindingResult, Model model) {
		if (bindingResult.hasErrors()) {
			logger.info("Returning empSave.jsp page");
			return "empSave";
		}
		logger.info("Returning empSaveSuccess.jsp page");
		model.addAttribute("emp", employee);
		emps.put(employee.getId(), employee);
		return "empSaveSuccess";
	}
}

Para usar um validador personalizado, primeiro precisamos injetá-lo na classe do controlador. Estamos usando autowiring de feijão do Spring para conseguir isso usando as anotações `@Autowired` e `@Qualifier`. Em seguida, precisamos ter um método que receberá WebDataBinder como argumento e configuraremos nosso validador personalizado para ser usado. Este método deve ser anotado com a anotação `@InitBinder`. Usar `@ModelAttribute` é outra maneira de adicionar nosso objeto de feijão ao Modelo. O restante do código é semelhante à implementação do controlador do cliente.

Mensagens de Erro de Validação de Formulário no Pacote de Recursos

É hora de examinar nosso pacote de recursos, onde temos diferentes tipos de mensagens a serem usadas para erros de validação. Arquivo message_en.properties:

#mensagens de erro definidas pela aplicação
id.required=Employee ID is required
name.required=Employee Name is required
role.required=Employee Role is required
negativeValue={0} can't be negative or zero

#mensagens de erro do framework Spring a serem usadas quando a conversão de dados do formulário para o bean falha
typeMismatch.int={0} Value must be an integer
typeMismatch.java.lang.Integer={0} must be an integer
typeMismatch={0} is of invalid format

#mensagens de aplicação para anotações, {ValidationClass}.{modelObjectName}.{field}
#o {0} é o nome do campo, outros campos estão em ordem alfabética, max e depois min  
Size.customer.name=Customer {0} should be between {2} and {1} characters long
NotEmpty.customer.email=Email is a required field
NotNull.customer.age=Customer {0} should be in years

#mensagens de classe de anotação genérica
Email=Email address is not valid
NotNull=This is a required field
NotEmpty=This is a required field
Past=Date should be Past

#Anotação de validação personalizada
Phone=Invalid format, valid formats are 1234567890, 123-456-7890 x1234

I have provided message key details in the comment itself, so I will skip them here. The only important point to note here is the way messages will be looked up, first key name {ValidationClass}.{modelObjectName}.{field} is looked up and if that is not found then {ValidationClass}.{modelObjectName} is looked up. If that is missing, then finally {ValidationClass} key is looked up. If nothing is found then the default message provided will be returned. Read more about resource messages at Spring Localization Example.

Páginas de Visualização com Formulário e Erros

Já que estamos usando a implementação de validação do framework Spring, precisaremos usar as tags de formulário do Spring para obter os erros e definir os nomes do bean e da variável do formulário. O código do nosso arquivo custSave.jsp é mostrado abaixo.

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="https://www.springframework.org/tags/form"
	prefix="springForm"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Customer Save Page</title>
<style>
.error {
	color: #ff0000;
	font-style: italic;
	font-weight: bold;
}
</style>
</head>
<body>

	<springForm:form method="POST" commandName="customer"
		action="save.do">
		<table>
			<tr>
				<td>Name:</td>
				<td><springForm:input path="name" /></td>
				<td><springForm:errors path="name" cssClass="error" /></td>
			</tr>
			<tr>
				<td>Email:</td>
				<td><springForm:input path="email" /></td>
				<td><springForm:errors path="email" cssClass="error" /></td>
			</tr>
			<tr>
				<td>Age:</td>
				<td><springForm:input path="age" /></td>
				<td><springForm:errors path="age" cssClass="error" /></td>
			</tr>
			<tr>
				<td>Gender:</td>
				<td><springForm:select path="gender">
						<springForm:option value="" label="Select Gender" />
						<springForm:option value="MALE" label="Male" />
						<springForm:option value="FEMALE" label="Female" />
					</springForm:select></td>
				<td><springForm:errors path="gender" cssClass="error" /></td>
			</tr>
			<tr>
				<td>Birthday:</td>
				<td><springForm:input path="birthday" placeholder="MM/dd/yyyy"/></td>
				<td><springForm:errors path="birthday" cssClass="error" /></td>
			</tr>
			<tr>
				<td>Phone:</td>
				<td><springForm:input path="phone" /></td>
				<td><springForm:errors path="phone" cssClass="error" /></td>
			</tr>
			<tr>
				<td colspan="3"><input type="submit" value="Save Customer"></td>
			</tr>
		</table>

	</springForm:form>

</body>
</html>

commandName="cliente" é usado para definir o nome do atributo do modelo sob o qual o objeto de formulário é exposto. Seu valor padrão é “comando” por padrão, portanto, devemos definir para o nome do atributo do modelo que estamos usando em nossas classes de controlador. springForm:errors é usado para renderizar os erros, se houver, encontrados quando a página é renderizada. O atributo path é usado para definir a propriedade do objeto a ser usada para a ligação de dados. O restante do código é HTML padrão com algum CSS para estilização de mensagens de erro. Nosso arquivo custSaveSuccess.jsp é dado abaixo.

<%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="fmt" uri="https://java.sun.com/jsp/jstl/fmt" %>

<%@ page session="false" %>
<html>
<head>
	<title>Customer Saved Successfully</title>
</head>
<body>
<h3>
	Customer Saved Successfully.
</h3>

<strong>Customer Name:${customer.name}</strong><br>
<strong>Customer Email:${customer.email}</strong><br>
<strong>Customer Age:${customer.age}</strong><br>
<strong>Customer Gender:${customer.gender}</strong><br>
<strong>Customer Birthday:<fmt:formatDate value="${customer.birthday}" type="date" /></strong><br>

</body>
</html>

Página JSP simples mostrando os valores do cliente se não houver erros de validação e esta página é retornada como resposta. Seu nome é empSave.jsp.

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="https://www.springframework.org/tags/form"
	prefix="springForm"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Employee Save Page</title>
<style>
.error {
	color: #ff0000;
	font-style: italic;
	font-weight: bold;
}
</style>
</head>
<body>

	<springForm:form method="POST" commandName="employee"
		action="save.do">
		<table>
			<tr>
				<td>Employee ID:</td>
				<td><springForm:input path="id" /></td>
				<td><springForm:errors path="id" cssClass="error" /></td>
			</tr>
			<tr>
				<td>Employee Name:</td>
				<td><springForm:input path="name" /></td>
				<td><springForm:errors path="name" cssClass="error" /></td>
			</tr>
			<tr>
				<td>Employee Role:</td>
				<td><springForm:select path="role">
						<springForm:option value="" label="Select Role" />
						<springForm:option value="ceo" label="CEO" />
						<springForm:option value="developer" label="Developer" />
						<springForm:option value="manager" label="Manager" />
					</springForm:select></td>
				<td><springForm:errors path="role" cssClass="error" /></td>
			</tr>
			<tr>
				<td colspan="3"><input type="submit" value="Save"></td>
			</tr>
		</table>

	</springForm:form>

</body>
</html>

Arquivo empSaveSuccess.jsp:

<%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
	<title>Employee Saved Successfully</title>
</head>
<body>
<h3>
	Employee Saved Successfully.
</h3>

<strong>Employee ID:${emp.id}</strong><br>
<strong>Employee Name:${emp.name}</strong><br>
<strong>Employee Role:${emp.role}</strong><br>

</body>
</html>

Testar a Aplicação de Validação de Formulário Spring MVC

Nossa aplicação está pronta para implantação e execução de alguns testes, implante-a em seu contêiner de servlet favorito. Estou usando o Apache Tomcat 7 e abaixo estão algumas imagens de páginas com mensagens de erro de validação. Com base nos seus dados de entrada, você também pode receber mensagens de erro diferentes. Isso é tudo para a validação de formulário do Spring MVC com diferentes métodos e usando pacotes de recursos para mensagens de erro localizadas. Você pode baixar o projeto de exemplo no link abaixo e brincar com ele para aprender mais.

Baixar Projeto de Validação de Formulário do Spring

Source:
https://www.digitalocean.com/community/tutorials/spring-validation-example-mvc-validator