Benutzereingabe-Validierung in Webanwendungen mit Spring MVC Form Validator
Wenn wir Benutzereingaben in einer Webanwendung akzeptieren, wird es notwendig, diese zu validieren. Wir können die Benutzereingabe auf der Clientseite mit JavaScript validieren, aber es ist auch notwendig, sie auf der Serverseite zu validieren, um sicherzustellen, dass wir gültige Daten verarbeiten, falls der Benutzer JavaScript deaktiviert hat.
Validierung mit Spring MVC Form Validator
Das Spring MVC Framework unterstützt standardmäßig die JSR-303-Spezifikationen, und alles, was wir tun müssen, ist, JSR-303 und seine Implementierungsabhängigkeiten in die Spring MVC-Anwendung einzufügen. Spring bietet auch die Annotation @Validator und die Klasse BindingResult an, durch die wir die von der Validator-Implementierung im Controller-Anfrage-Handler erzeugten Fehler erhalten können. Wir können unsere eigenen benutzerdefinierten Validator-Implementierungen auf zwei Arten erstellen – die erste besteht darin, eine Annotation zu erstellen, die den JSR-303-Spezifikationen entspricht, und deren Validator-Klasse zu implementieren. Der zweite Ansatz besteht darin, das Interface org.springframework.validation.Validator zu implementieren und es im Controller mithilfe der Annotation @InitBinder als Validator festzulegen. Lassen Sie uns ein einfaches Spring MVC-Projekt im Spring Tool Suite erstellen, in dem wir die JSR-303-Spezifikationen mit dem Implementierungsartefakt hibernate-validator verwenden. Wir werden auf Annotations basierende Formularvalidierung verwenden und unseren eigenen benutzerdefinierten Validator basierend auf den JSR-303-Spezifikationsstandards erstellen. Wir werden auch unsere eigene benutzerdefinierte Validator-Klasse erstellen, indem wir das Validator-Interface implementieren und es in einer der Controller-Handler-Methoden verwenden.
Spring MVC Form Validator
Unsere endgültige pom.xml-Datei sieht wie folgt aus. Neben den Standard-Spring-MVC-Artefakten haben wir validation-api und hibernate-validator-Abhängigkeiten im Projekt.
<?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>
Bereitstellungsbeschreibung
Beim Erstellen eines Spring MVC-Projekts von STS werden zwei Kontextkonfigurationsdateien erstellt. Wir haben dies etwas bereinigt und haben nur eine Spring-Bean-Konfigurationsdatei. Unsere finale web.xml-Datei sieht wie folgt aus:
<?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">
<!-- Verarbeitet Anwendungsanfragen -->
<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>
Spring Bean-Konfigurationsdatei
Normalerweise schauen wir uns die Spring-Verkabelungen zuletzt an, aber dieses Mal haben wir nicht viele Konfigurationen in der Spring-Bean-Konfigurationsdatei. Unsere finale spring.xml-Datei sieht wie folgt aus.
<?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-Kontext: definiert die Anfrageverarbeitungsinfrastruktur dieses Servlets -->
<!-- Ermöglicht das Spring MVC @Controller-Programmiermodell -->
<annotation-driven />
<!-- Verarbeitet HTTP GET-Anfragen für /resources/**, indem statische Ressourcen im Verzeichnis ${webappRoot}/resources effizient bereitgestellt werden -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Löst von @Controllers ausgewählte Ansichten zur Darstellung als .jsp-Ressourcen im Verzeichnis /WEB-INF/views auf -->
<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>
Der einzige wichtige Punkt ist der employeeValidator-Bean, den wir in einen der Controller einfügen werden, und der messageSource-Bean, um die lokalisierten Daten aus den Ressourcenbündeln zu lesen. Der Rest dient zur Unterstützung von Annotationen, View-Resolvern und zum Bereitstellen von Paketen zum Scannen von Controller-Klassen und anderen Komponenten.
Modelklassen
Kundenmodellklasse
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
}
// Getter und Setter
}
Mitarbeitermodellklasse
package com.journaldev.spring.form.model;
public class Employee {
private int id;
private String name;
private String role;
// Getter und Setter
}
Eigene Validator-Implementierungen
Telefon-Validator
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 {};
}
package com.journaldev.spring.form.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PhoneValidator implements ConstraintValidator<Phone, String> {
@Override
public void initialize(Phone paramA) {
}
@Override
public boolean isValid(String phoneNo, ConstraintValidatorContext ctx) {
if (phoneNo == null) {
return false;
}
// validiere Telefonnummern des Formats "1234567890"
if (phoneNo.matches("\\d{10}")) return true;
// validiere Telefonnummern mit -, . oder Leerzeichen
else if (phoneNo.matches("\\d{3}[-\\.\\s]\\d{3}[-\\.\\s]\\d{4}")) return true;
// validiere Telefonnummern mit Durchwahllänge von 3 bis 5
else if (phoneNo.matches("\\d{3}-\\d{3}-\\d{4}\\s(x|(ext))\\d{3,5}")) return true;
// validiere Telefonnummern, bei denen die Vorwahl in Klammern steht ()
else if (phoneNo.matches("\\(\\d{3}\\)-\\d{3}-\\d{4}")) return true;
// gebe false zurück, wenn nichts mit der Eingabe übereinstimmt
else return false;
}
}
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 {
@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 darf nicht negativ sein");
}
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "name.required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "role", "role.required");
}
}
Controller-Klassen
CustomerController-Klasse
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("Rückgabe der custSave.jsp-Seite");
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("Rückgabe der custSave.jsp-Seite");
return "custSave";
}
logger.info("Rückgabe der custSaveSuccess.jsp-Seite");
model.addAttribute("customer", customer);
customers.put(customer.getEmail(), customer);
return "custSaveSuccess";
}
}
Kundenspeicher-Erfolgsseite (custSaveSuccess.jsp)
<%@ page session="false" %>
<html>
<head>
<title>Kunde erfolgreich gespeichert</title>
</head>
<body>
<h3>
Kunde erfolgreich gespeichert.
</h3>
<!-- Kundendetails anzeigen -->
</body>
</html>
Mitarbeiterspeicherseite (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>Mitarbeiterspeicherseite</title>
<style>
.error {
color: #ff0000;
font-style: italic;
font-weight: bold;
}
</style>
</head>
<body>
<springForm:form method="POST" commandName="employee" action="save.do">
<table>
<!-- Formularfelder -->
</table>
</springForm:form>
</body>
</html>
Mitarbeiterspeicher-Erfolgsseite (empSaveSuccess.jsp)
<%@ page session="false" %>
<html>
<head>
<title>Mitarbeiter erfolgreich gespeichert</title>
</head>
<body>
<h3>
Mitarbeiter erfolgreich gespeichert.
</h3>
<!-- Mitarbeiterdetails anzeigen -->
</body>
</html>
Testen der Spring MVC Formularvalidierungsanwendung
Unsere Anwendung ist bereit zum Bereitstellen und Testen. Deployen Sie es in Ihrem bevorzugten Servlet-Container. Wir verwenden Apache Tomcat 7.
Fazit
Das ist eine Anleitung zur Spring MVC Formularvalidierung mit verschiedenen Methoden und unter Verwendung von Ressourcenbündeln für lokalisierte Fehlermeldungen – Benutzereingabe-Validierung in Webanwendungen.