今天在学习 Spring 实战 第四版的第五章过程中,使用到了 spring MVC 对 Java 校验 API 的支持,添加了 javax.validation 中的注释来实现对 Spitter 类属性的输入验证。
因为书中并没有详细的代码,加上之前没有进行任何相关的学习,因此只能一步步靠自己摸索出来。
首先贴上 Spitter 类:
package org.fisherman.spittr.pojo;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* Created by fisherman
* 2018/8/30
*/
public class Spitter {
private Long id;
@NotEmpty
@Size(min = 5, max = 16)
private String firstName;
@NotEmpty
@Size(min = 5, max = 25)
private String lastName;
@NotEmpty
@Size(min = 2, max = 30)
private String username;
@NotEmpty
@Size(min = 2, max = 30)
private String password;
public Spitter(Long id, String firstName, String lastName, String username, String password) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.username = username;
this.password = password;
}
public Spitter(String firstName, String lastName, String username, String password) {
this.id = 0L;
this.firstName = firstName;
this.lastName = lastName;
this.username = username;
this.password = password;
}
public Spitter() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
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;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Spittle) {
return EqualsBuilder.reflectionEquals(this, obj, "id");
} else {
return false;
}
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this, "id");
}
}
然后是 SpitterController 类:
package org.fisherman.spittr.web;
import org.fisherman.spittr.data.SpitterRepository;
import org.fisherman.spittr.pojo.Spitter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.validation.Valid;
/**
* Created by fisherman
* 2018/8/30
*/
@Controller
@RequestMapping(value = "/spitter")
public class SpitterController {
private SpitterRepository spitterRepository;
public SpitterController() {
}
@Autowired
public SpitterController(SpitterRepository spitterRepository) {
this.spitterRepository = spitterRepository;
}
@RequestMapping(value = "/register", method = RequestMethod.GET)
public String showRegistrationForm() {
return "registerForm";
}
@RequestMapping(value = "/register", method = RequestMethod.POST)
public String processRegistration(@Valid Spitter spitter,
Errors errors) {
if (errors.hasErrors()) {
return "registerForm";
}
spitterRepository.save(spitter);
// redirect 到该新增用户的个人页
return "redirect:/spitter/" + spitter.getUsername();
}
@RequestMapping(value = "/{username}", method = RequestMethod.GET)
public String showSpitterProfile(@PathVariable String username, Model model) {
Spitter spitter = spitterRepository.findByUsername(username);
model.addAttribute(spitter);
return "profile";
}
}
重点关注 SpitterController 类中的 processRegistration() 方法,有两点需要注意:
- 在需要验证的传入参数,也就是表单提交的参数,Spitter 之前需要加上 @Valid 注释,告知 Spring 需要确保这个对象满足校验限制;
- 在 Spitter 属性上添加了校验限制并不能阻止表单提交,因此即使用户没有填写某个域或者某个域给定的值超过了最大长度,processRegistration() 方法依然会被调用,这就要求我们需要处理校验的错误;
- 校验出现的错误可以通过 Errors 对象进行访问,Errors 参数需要紧跟在带有 @Valid 注解的参数后面,即跟在需要进行校验的参数后面。
在完成代码初步编写并进行测试时,发现空表单的提交会直接 redirect 到 /spitter/ 这个路径,debug后验证了自己的想法,原因就是 validation 没有作用,errors 的数量为0,因此 errors.hasErrors() 方法返回false,从此开始了寻找解决办法之旅。
在网上搜索时发现出现这种问题的情况貌似不多,因此仔细看书后发现书中提示了一点:在 Spring MVC 中要使用 Java 校验 API的话,不需要什么额外配置,只需要保证在 classpath 下包含这个 Java 校验 API 的实现,比如 Hibernate Validator。
因此在 maven 中添加了 hibernate validator 的依赖,然后在 Spring 的配置文件中需要加入 启用此 validator 的 bean,applicationContext.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:p="http://www.springframework.org/schema/p"
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:annotation-config/>
<mvc:annotation-driven validator="localValidatorFactoryBean"/>
<context:component-scan base-package="org.fisherman.spittr"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<bean id="localValidatorFactoryBean" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/views/" p:suffix=".jsp"/>
</beans>
其中 localValidatorFactoryBean 为生成 validator 的工厂 bean 类,需要注入,然后需要添加<mvc:annotation-driven/>的配置,而且需要制定 validator 为注入的 localValidatorFactoryBean 类。
最后需要注意的一点是,光引入 Hibernate Validator 并不行,还有其所需要的依赖需要引入,共有如下几个:
<!-- validation -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.2.Final</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.3.2.Final</version>
</dependency>
<dependency>
<groupId>com.fasterxml</groupId>
<artifactId>classmate</artifactId>
<version>1.3.4</version>
</dependency>
在我的使用过程中,只在 maven 中添加依赖会有其他问题,比如 tomcat 容器无法找到这些类,如果出现此问题,需要将依赖的这些 jar 包全部添加到 tomcat 下的 lib 文件夹下,到此,就不会再有其他问题。