Spring表单校验

从Spring 3.0开始,Spring对Java校验API(Java Validation API,又称JSR-303)提供了支持。在Spring MVC中要使用Java校验API的话,并不需要什么额外的配置。只要保证在类路径下包含这个Java API的实现即可,比如Hibernate Validator。

引入hibernate-validator:

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.4.Final</version>
</dependency>

校验注解

所有的注解都位于javax.validation.constraintsorg.hibernate.validator.constraints包中。下表列出了这些校验注解。

注解描述
@Null限制只能为null
@NotNull限制必须不为null
@AssertFalse限制必须为false
@AssertTrue限制必须为true
@DecimalMax(value)限制必须为一个不大于指定值的数字
@DecimalMin(value)限制必须为一个不小于指定值的数字
@Digits(integer,fraction)限制必须为一个小数,且整数部分的位数不能超过integer, 小数部分的位数不能超过fraction
@Future限制必须是一个将来的日期
@Past限制必须是一个过去的日期
@Max(value)限制必须为一个不大于指定值的数字
@Min(value)限制必须为一个不小于指定值的数字
@Past限制必须是一个过去的日期
@Pattern(value)限制必须符合指定的正则表达式
@Size(max,min)限制字符长度必须在min到max之间
@SafeHtml字符串是安全的html
@URL字符串是合法的URL
@NotBlank字符串必须有字符
@NotEmpty字符串不为NULL,集合有字符
@AssertFalse必须是false
@AssertTrue必须是true

使用校验注解配置实体类

新建一个表单实体类,并加上注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;

public class Form {
@NotNull
@Size(min = 5, max = 16, message = "{name.msg}")
private String name;

@NotNull()
@Email(message = "{email.msg}")
private String email;

@NotNull
@Size(min = 5, max = 16, message = "{password.msg}")
private String password;

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 String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}
}

message属性用于添加国际化支持,接下来需要做的就是创建一个名为ValidationMessages.properties的文件(默认叫这个,不区分大小写,放在src/main/resources路径下):

1
2
3
name.msg=\u7528\u6237\u540D\u957F\u5EA6\u4E3A{min}\u5230{max}\u4E2A\u5B57\u7B26
password.msg=\u5BC6\u7801\u957F\u5EA6\u4E3A{min}\u5230{max}\u4E2A\u5B57\u7B26
email.msg=\u90AE\u7BB1\u683C\u5F0F\u4E0D\u5408\u6CD5

ValidationMessages.properties文件中每条信息的key值对应于注解中message属性占位符的 值。同时,最小和最大长度在ValidationMessages.properties文件中有自己的占位符——{min}{max}——它们会引用@Size注解上所设置的min和max属性。

自定义校验规则

上面的注解都是较为简单的注解,实际编程中校验的规则可能五花八门。当自带的这些注解无法满足我们的需求时,我们也可以自定义校验注解。下面是一个自定义校验注解的基本格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import javax.validation.Constraint;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target

@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MyConstraintValidator.class)
public @interface MyConstraint {

String message();

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}

其中@Constraint注解表明这个注解是用于规则校验的,validatedBy属性表明用什么去校验,这里我们指定的类为MyConstraintValidator。注解还包含了三个书属性,属性message指定当校验不通过的时候提示什么信息。

接下来编写MyConstraintValidator,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class MyConstraintValidator implements ConstraintValidator<MyConstraint, Object> {

@Override
public void initialize(MyConstraint myConstraint) {
System.out.println("my validator init");
}

@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
System.out.println(o);
return false;
}
}

MyConstraintValidator实现了ConstraintValidator接口,该接口必须指定两个泛型,第一个泛型指的是上面定义的注解类型,第二个泛型表示校验对象的类型。MyConstraintValidator实现了ConstraintValidator接口的initialize方法和isValid方法。

initialize方法用于该校验初始化的时候进行一些操作;isValid方法用于编写校验逻辑,第一个参数为需要校验的值,第二个参数为校验上下文。

Spring JSP库

为了使用Spring JSP库,需要在JSP页首加入:

1
2
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>

引入依赖:

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/jstl/jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

相关标签如下表所示:

JSP标签描述
<sf:checkbox>渲染成一个HTML <input>标签,其中type属性设置为checkbox
<sf:checkboxes>渲染成多个HTML <input>标签,其中type属性设置为checkbox
<sf:errors>在一个HTML <span>中渲染输入域的错误
<sf:form>渲染成一个HTML <form>标签,并为其内部标签暴露绑定路径,用于数据绑定
<sf:hidden>渲染成一个HTML <input>标签,其中type属性设置为hidden
<sf:input>渲染成一个HTML <input>标签,其中type属性设置为text
<sf:label>渲染成一个HTML <label>标签
<sf:option>渲染成一个HTML <option>标签,其selected属性根据所绑定的值进行设置
<sf:options>按照绑定的集合、数组或Map,渲染成一个HTML <option>标签的列表
<sf:password>渲染成一个HTML <input>标签,其中type属性设置为password
<sf:radiobutton>渲染成一个HTML <input>标签,其中type属性设置为radio
<sf:radiobuttons>渲染成多个HTML <input>标签,其中type属性设置为radio
<sf:select>渲染为一个HTML <select>标签
<sf:textarea>渲染为一个HTML <textarea>标签
用Spring JSP标签创建一个register.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;chartset=utf-8">
<title>leanote 蚂蚁笔记</title>
</head>
<style>
input{margin-top:5px;}
label.error{color:red;}
span.error{color:red;}
input.error{border:1px solid red;}
</style>
<body>
<sf:form method="post" commanName="form"
action="${pageContext.request.contextPath}/register">
<sf:label path="name" cssErrorClass="error">用户名:</sf:label>
<sf:input path="name" cssErrorClass="error"/>
<sf:errors path="name" cssClass="error"/><br/>
邮箱:<sf:input path="email"/><sf:errors path="email" cssClass="error"/><br/>
密码:<sf:password path="password"/><sf:errors path="password" cssClass="error"/><br/>
<input type="submit" value="注册"/>
</sf:form>
</body>
</html>

<sf:form>会渲染为一个HTML<form>标签,也可以通过commandName属性构建针对某个模型对象的上下文信息。这里设为form(待会在controller中传递到此页面)。

cssClass属性可以给标签加上样式Class,用于在CSS中对其选中并修改样式。

path属性指向实体类form对应的属性名称。如果将<sf:errors/>标签的path属性设置为*的话,其将显示所有不满足校验的提示信息。

cssErrorClass属性指定校验不通过时候除了<sf:errors/>标签外的标签样式。

编写controller

Regester控制器如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import javax.validation.Valid;
import mrbird.mvc.entity.Form;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class LeanoteController {
@RequestMapping(value = "/registerindex", method = RequestMethod.GET)
public String register(Model model) {
//对应<sf:form/>标签的commandName属性值
model.addAttribute(new Form());
return "register";
}

@RequestMapping(value = "/register", method = RequestMethod.POST)
//form参数添加了@Valid注解,这会告知Spring,需要确保这个对象满足校验限制。
//Errors参数要紧跟在带有@Valid注解的参数后面
public String submit(@Valid Form form, BindingResult result) {
if (result.hasErrors()) {
// 输出校验失败信息
result.getAllErrors().stream().forEach(e -> {
FieldError fieldError = (FieldError) e;
System.out.println(((FieldError) e).getField() + " " + e.getDefaultMessage());
}
);
//出错时回到注册页面,这里不能够用重定向,不然看不到错误提示信息
return "register";
}
//校验通过,重定向到/success/{name}
return "redirect:/success/" + form.getName();
}

@RequestMapping(value = "/success/{name}", method = RequestMethod.GET)
public String success(@PathVariable String name, Model model) {
model.addAttribute(name);
return "success";
}
}

部署项目,访问:http://localhost:8080/spring/registerindex:

65384397-file_1487996138511_af03.gif


TOP