SpringMvc 对提交的数据进行验证
在网页上提交数据到后端,除了在前端用 js 对相关数据进行验证之外,考虑到安全性,服务器端也是必须需要验证的。尤其是对于当前比较流行的前后端分离的开发方案,前端调用后端的接口提交数据,后端的接口需要对提交过来的数据进行验证无误,才能确保业务的正常开展。SpringMvc 借助第三方 hibernate-validator 组件,能够减轻后端验证数据的代码量,提高开发效率。
本博客主要通过代码的方式,简单介绍 SpringMvc 如何使用第三方 hibernate-validator 的 jar 包组件,实现对提交到后端数据的简单验证,在本博客的最后会提交源代码的下载。
有关 hibernate-validator 的 jar 包组件的详细细节,请查看其官网:https://github.com/hibernate/hibernate-validator
一、搭建工程
新建一个 maven 项目,导入相关 jar 包,我所导入的 jar 包都是最新的,内容如下:
有关具体的 jar 包地址,可以在 https://mvnrepository.com 上进行查询。
<dependencies>
<!--导入 servlet 相关的 jar 包-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<!--导入 Spring 核心 jar 包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<!--导入 SpringMvc 的 jar 包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.18</version>
</dependency>
<!--导入 jackson 相关的 jar 包-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.1</version>
</dependency>
<!--导入校验相关的 jar 包-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Final</version>
</dependency>
</dependencies>
需要注意最后导入的 hibernate-validator 这个 jar 包,这个是本篇博客的重点 jar 包。另外需要的是对于 hibernate-validator 的版本,6 以上的版本必须使用 tomcat 8 以上的版本才能支持,所以这里不能使用官网提供的 tomcat 插件在运行 demo ,因为目前 tomcat 插件的最高版本为 tomcat7 ,不能满足要求,本篇博客采用的 tomcat 9.5 来运行 demo 。
配置好引用的 jar 包后,打开右侧的 Maven 窗口,刷新一下,这样 Maven 会自动下载所需的 jar 包文件。
搭建好的项目工程整体目录比较简单,具体如下图所示:
com.jobs.config 包下存储的是 SpringMvc 的配置文件和 Servlet 的初始化文件
com.jobs.controller 包下存储的是用于提供 api 接口的类
com.jobs.domain 包下存储的是用来接收数据的实体类
com.jobs.group 包下存储的是空接口,主要是对验证字段进行分组
web 目录下放置的是网站文件,只有一个静态页面和一些 js 文件
二、字段验证配置细节
有关 SpringMvc 纯注解配置( ServletInitConfig 和 SpringMvcConfig)内容,跟之前的博客相比,没有任何变化,这里就不介绍了。我们先从简单的内容进行介绍,首选介绍两个分组的接口,内容如下:
package com.jobs.group;
public interface GroupValidateA {
}
package com.jobs.group;
public interface GroupValidateB {
}
这是两个空的接口,没有任何内容。本博客前端页面提交过来的数据,会自动封装到实体类中,我们会对实体类的相关字段进行数据验证。但是并不是每次都需要验证所有的字段,比如添加用户的时候,可能需要验证提交过来的所有字段,包括密码等内容。但是在修改用户的时候,并不需要修改密码,所以不需要对密码进行验证。此时就需要对用户需要验证的字段进行分组,比如把添加用户时需要验证的字段标记上 A 组,把修改用户时需要验证的字段标记上 B 组。当然实体类中的一个字段,可以同时属于多个验证组,比如同时属于 A 组和 B 组。
在本篇博客中,我们验证提交过来的员工信息,在 Employee 实体类和 Department 实体类中对相关字段进行分组验证。
package com.jobs.domain;
import com.jobs.group.GroupValidateA;
import com.jobs.group.GroupValidateB;
import javax.validation.Valid;
import javax.validation.constraints.*;
public class Employee {
//员工名称
@Size(min = 2, max = 10,
message = "员工姓名不能为空,且字符长度必须在 2 到 10 之间",
groups = {GroupValidateA.class, GroupValidateB.class})
private String name;
//员工年龄
@Min(value = 18, message = "员工的年龄不能小于 18 岁", groups = {GroupValidateA.class})
@Max(value = 60, message = "员工的年龄不能大于 60 岁", groups = {GroupValidateA.class})
@NotNull(message = "请填写员工的年龄", groups = {GroupValidateA.class})
private Integer age;
//性别
@NotNull(message = "请选择员工的性别", groups = {GroupValidateA.class, GroupValidateB.class})
private Integer gender;
@Valid
private Department department;
//这里省略 get 和 set 方法...
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", gender=" + gender +
", department=" + department +
'}';
}
}
package com.jobs.domain;
import com.jobs.group.GroupValidateA;
import javax.validation.constraints.Size;
//所属部门
public class Department {
//部门名称
@Size(min = 3, max = 10,
message = "部门名称不能为空,且字符长度必须在 3 到 10 之间",
groups = {GroupValidateA.class})
private String departName;
//职位
private String position;
//这里省略 get 和 set 方法...
@Override
public String toString() {
return "Department{" +
"departName='" + departName + '\'' +
", position='" + position + '\'' +
'}';
}
}
员工 Employee 实体类,有一个属性是 Dempartment ,这又是一个实体类。上边的 @Size ,@Min,@Max,@NotNull 都是字段数据验证的注解,从其 message 内容就很容易看出其验证的具体功能。需要注意的是:由于 Department 是一个实体类,如果想要验证 Department 类中的相关字段,需要在 Employee 类的 Department 字段是增加 @Valid 注解。
上面的验证字段,除了 Department 类中的 position 字段没有添加验证注解外,其它字段都被归属到了 GroupValidateA 验证组,同时又把 Employee 类的 name 和 gender 也归属到了 GroupValidateB 组。下面让我们看一下 TestController 的内容:
package com.jobs.controller;
import com.jobs.domain.Employee;
import com.jobs.group.GroupValidateA;
import com.jobs.group.GroupValidateB;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@RequestMapping("/test")
@RestController
public class TestController {
//验证 GroupValidateA 组的规则
@PostMapping(value = "/validate1")
public String validateRequest1(
@Validated({GroupValidateA.class}) Employee employee, Errors errors) {
System.out.println(employee);
if (errors.hasErrors()) {
List<FieldError> fieldErrors = errors.getFieldErrors();
System.out.println("验证发现的错误如下:");
for (FieldError error : fieldErrors) {
System.out.println(error.getField() + "---->" + error.getDefaultMessage());
}
Set<String> setError =
fieldErrors.stream().map(s -> s.getDefaultMessage())
.collect(Collectors.toSet());
return setError.toString();
} else {
return "提交成功,没有任何问题";
}
}
//验证 GroupValidateB 组的规则(只验证两个字段:员工姓名和员工性别)
@PostMapping(value = "/validate2")
public String validateRequest2(
@Validated({GroupValidateB.class}) Employee employee, Errors errors) {
System.out.println(employee);
if (errors.hasErrors()) {
List<FieldError> fieldErrors = errors.getFieldErrors();
System.out.println("验证发现的错误如下:");
for (FieldError error : fieldErrors) {
System.out.println(error.getField() + "---->" + error.getDefaultMessage());
}
Set<String> setError =
fieldErrors.stream().map(s -> s.getDefaultMessage())
.collect(Collectors.toSet());
return setError.toString();
} else {
return "提交成功,没有任何问题";
}
}
}
TestController 开发了两个接口,其中 /test/validate1 接口只验证 GroupValidateA 组的字段,/test/validate2 接口只验证 GroupValidateB 组的字段。
三、前端页面细节
在本 Demo 的首页 index.html 编写了两个 ajax 数据请求,分别请求 TestController 中的两个接口,分别验证 GroupValidateA 和 GroupValidate 两个组的提交过来的字段内容。index.html 和所引用的 apitest.js 具体细节如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SpringMvc提交数据验证</title>
</head>
<body>
<fieldset>
<legend>对 GroupValidateA 组设置的字段进行验证</legend>
员工姓名:<input type="text" id="name1"/><br/>
员工年龄:<input type="text" id="age1"/><br/>
员工性别:
<select id="gender1">
<option value="" selected>请选择</option>
<option value="1">男</option>
<option value="0">女</option>
</select><br/>
部门名称:<input type="text" id="depname1"/><br/>
工作职位:<input type="text" id="position1"/><br/>
<input type="button" id="btn1" value="提交">
</fieldset>
<hr/>
<fieldset>
<legend>对 GroupValidateB 组设置的字段进行验证</legend>
员工姓名:<input type="text" id="name2"/><br/>
员工年龄:<input type="text" id="age2"/><br/>
员工性别:
<select id="gender2">
<option value="" selected>请选择</option>
<option value="1">男</option>
<option value="0">女</option>
</select><br/>
部门名称:<input type="text" id="depname2"/><br/>
工作职位:<input type="text" id="position2"/><br/>
<input type="button" id="btn2" value="提交">
</fieldset>
<script src="./js/jquery-3.6.0.min.js"></script>
<script src="js/apitest.js"></script>
</body>
</html>
$(function () {
$('#btn1').click(function () {
let name1 = $('#name1').val().trim();
let age1 = $('#age1').val().trim();
let gender1 = $('#gender1').val();
let depname1 = $('#depname1').val().trim();
let position1 = $('#position1').val().trim();
$.ajax({
type: "post",
url: "/test/validate1",
dataType: "text",
data:
{
name: name1,
age: age1,
gender: gender1,
'department.departName': depname1,
'department.position': position1
},
success: function (data) {
alert("返回的数据:" + data);
},
error: function (data) {
console.log(data.responseText);
}
});
});
$('#btn2').click(function () {
let name2 = $('#name2').val();
let age2 = $('#age2').val();
let gender2 = $('#gender2').val();
let depname2 = $('#depname2').val();
let position2 = $('#position2').val();
$.ajax({
type: "post",
url: "/test/validate2",
dataType: "text",
data:
{
name: name2,
age: age2,
gender: gender2,
'department.departName': depname2,
'department.position': position2
},
success: function (data) {
alert("返回的数据:" + data);
},
error: function (data) {
console.log(data.responseText);
}
});
});
})
启动 Demo 网站,分别进行相关数据的填写或者清除,提交数据,查看后端验证效果。
到此为止,有关 SpringMvc 使用第三方 hibernate-validator 的 jar 包组件,实现对提交到后端数据的简单验证,基本介绍完毕。
本博客 Demo 的源代码下载地址为:https://files.cnblogs.com/files/blogs/699532/SpringMvc_validate.zip