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 包文件。

搭建好的项目工程整体目录比较简单,具体如下图所示:

image

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

posted @ 2022-04-22 22:52  乔京飞  阅读(9950)  评论(0编辑  收藏  举报