Spring Boot系列(八)、JSR-303基于由Hibernate-Validitor实现的后端服务器数据校验

1、JSR-303简介

JSR是Java Specification Requests的缩写,意思是Java 规范提案,JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是Hibernate Validator。

此实现与 Hibernate ORM 没有任何关系。 JSR 303 用于对 Java Bean 中的字段的值进行验证。 Spring MVC 3.x 之中也大力支持 JSR-303,可以在控制器中对表单提交的数据方便地验证。

即,JSR 303,Bean Validation规范 ,为Bean验证定义了元数据模型API。默认的元数据模型是通过Annotations来描述的,但是也可以使用XML来重载或者扩展, 推荐使用注解的方式进行验证。

2、JSR-303常用校验注解规则

空检查:

注解说明
@Null元素必须为null
@NotNull元素必须不为null
@NotBlank(message)字符串不能为null,且trim后不能为空字符串(长度非0),message加以说明
@NotEmpty(message)被注释的字符串、集合、数组不能为空(长度非0)

Boolean检查 :

注解说明
@AsserrtFalse验证Boolean对象是否为false
@AssertTrue验证Boolean对象是否为true

长度检查 :

注解说明
@Size(min,max,message)元素大小必须在指定范围内,message加以说明
@Length(min,max,message)必须是字符串,其值在指定范围内,message加以说明

日期检查 :

注解说明
@Past元素必须是一个过去的日期
@Furture元素必须是一个将来的日期
@Pattern元素符合指定的正则表达式

数值检查 (建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为 “”,Integer为null ):

注解说明
@Min(value)元素必须为数字,其值大于等于指定value
@Max(value)元素必须为数字,其值小于等于指定value
@DecimalMax(value)元素必须为数字,最大值为指定value
@DecimalMin(value)元素必须为数字,最小值为指定value
@Digits(integer,fraction)元素必须为数字,其值必须在可接受的范围内

以上是Bean Validation 中内置的 constraint。

Hibernate Validator 附加的 constraint

注解说明
@Email元素必须为电子邮箱格式
@Range元素在合适的范围内
@SafeHtml元素必须是安全Html
@URL元素必须是有效URL

3、JSR-303如何使用?

3.1、首先导入相关依赖(规范实现

JSR 303 是Bean验证的规范 ,Hibernate Validator 是该规范的参考实现,如果只有规范没有实现,则校验规范不会生效。

pom.xml

		<!--JSR-303校验规范-->
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </dependency>

        <!--JSR-303基于由Hibernate-validator规范实现-->
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.22.Final</version>
        </dependency>

3.2、对实体类SystemUserinfo部分字段进行校验注解

package com.kdcrm.pojo;

import javax.validation.constraints.NotEmpty;
import java.io.Serializable;

public class SystemUserinfo implements Serializable {

    private String userinfoUid;

    @NotEmpty(message = "用户名不能为空")
    private String userinfoLoginid;

    private String userinfoName;

    @NotEmpty(message = "登录密码不能为空")
    private String userinfoPassword;

    private String userinfoSex;

    private String userinfoEmail;

    private String userinfoMobile;

    private Short userinfoStatus;

    private String userinfoRoleid;

    private SystemRole systemRole;
    
    //setter和getter方法省略
}

我们暂时先对用户名(登录id)和登录密码两个字段进行@NotEmpty注解,用于登录界面用户名和密码的非可校验注解。

3.3、编写登录界面视图login.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>易买网 - 登录界面</title>
<link type="text/css" rel="stylesheet" href="css/style.css" />
<script type="text/javascript" src="scripts/function.js"></script>
</head>
<body>

<div id="login" class="wrap">
	<div class="shadow">
		<em class="corner lb"></em>
		<em class="corner rt"></em>
		<div class="box">
			<h1>欢迎回到易买网</h1>
			<form id="loginForm" method="post" action="doLogin" onsubmit="return checkForm(this)">
				<table>
					<tr>
						<td class="field">用户名:</td>
						<td><input class="text" type="text" name="userinfoLoginid" onfocus="FocusItem(this)" onblur="CheckItem(this);" /><span style="display: inline-block" th:text="${userinfoLoginid}"></span></td>
					</tr>
					<tr>
						<td class="field">登录密码:</td>
						<td><input class="text" type="password"  name="userinfoPassword" onfocus="FocusItem(this)" onblur="CheckItem(this);" /><span style="display: inline-block" th:text="${userinfoPassword}"></span></td>
					</tr>
					<tr>
						<td></td>
						<td><label class="ui-green"><input type="submit" name="submit" value="立即登录" />
						</label>
							<label th:text="${MSG}" style="color: red"></label>
						</td>
					</tr>
				</table>
			</form>
		</div>
	</div>
</div>
</body>
</html>

在这里插入图片描述
登录界面:
在这里插入图片描述

3.4、编写控制层SystemUserinfoController(登录控制方法)

    //跳转至登录界面
	@GetMapping("/toLogin")
    public String toLogin(){
        return "login";
    }
    
    //对登录信息进行处理
    @RequestMapping("/doLogin")
    public ModelAndView doLogin(@Valid SystemUserinfo systemUserinfo, BindingResult bindingResult, HttpServletRequest request, ModelAndView mav){

        //当前是否是错误
        if(bindingResult.hasErrors()) {
            //获取所有错误信息
            List<ObjectError> listError = bindingResult.getAllErrors();
            //创建存储错误信息的map
            Map<String, String> mapError = new HashMap<>();
            //遍历错误信息集合
            for (ObjectError objectError : listError) {
                //强转为FieldError类型(用于获取错误字段名)
                FieldError fieldError = (FieldError) objectError;
                //校验字段 : 校验信息
                System.out.println(fieldError.getField() + "\t" + objectError.getDefaultMessage());
                //将要校验的字段和校验信息存入到map集合中
                mapError.put(fieldError.getField(), objectError.getDefaultMessage());
                //将map集合存入到ModelAndView中(其实内部机制也是将其存入到request作用域中)
                mav.addAllObjects(mapError);
            }
            mav.setViewName("login");
            return mav;
        }else {
            SystemUserinfo systemUserinfo1 = systemUserinfoService.login(systemUserinfo);
            if(systemUserinfo1 != null){
                System.out.println("登录成功!");
                mav.addObject("USERS",systemUserinfo1);
                mav.setViewName("redirect:/selectAll");
            }else {
                System.out.println("输入了,但是用户名或密码错误!");
                mav.addObject("MSG","用户名或密码错误!");
                mav.setViewName("login");
            }
            return mav;
        }
    }

在这里插入图片描述
在这里插入图片描述
实际上此处将出现错误信息的字段名和校验信息存入到request作用中还有一种简单的做法,就是将字段名和错误信息直接存入到request作用域中,如下:

			//获取所有错误信息
            List<ObjectError> listError = bindingResult.getAllErrors();
          
            //遍历错误信息集合
            for (ObjectError objectError : listError) {
                //强转为FieldError类型(用于获取错误字段名)
                FieldError fieldError = (FieldError) objectError;
                //校验字段 : 校验信息
                System.out.println(fieldError.getField() + "\t" + objectError.getDefaultMessage());
               //直接存入request作用域中
               request.setAttribute(fieldError.getField(),objectError.getDefaultMessage());
            }

3.5、登录校验测试

在这里插入图片描述
查看控制台:
在这里插入图片描述

3.6、对实体类SystemUserinfo全部字段进行校验注解

上述登录演示说明我们的在后端的校验生效了,但是我们要考虑全面,对于实体类SystemUserinfo中的字段我们不可能只针对登录需要的两个字段进行校验,在添加、修改用户信息时,基本就需要用到全部字段的校验了,所有我们要对SystemUserinfo实体类中的所有字段进行非空注解信息,如下:

package com.kdcrm.pojo;

import javax.validation.constraints.NotEmpty;
import java.io.Serializable;

public class SystemUserinfo implements Serializable {
    //自动生成不用校验
    private String userinfoUid;

    @NotEmpty(message = "用户名不能为空")
    private String userinfoLoginid;

    @NotEmpty(message = "名字不能为空")
    private String userinfoName;

    @NotEmpty(message = "密码不能为空")
    private String userinfoPassword;

    @NotEmpty(message = "性别不能为空")
    private String userinfoSex;

    @NotEmpty(message = "邮箱不能为空")
    private String userinfoEmail;

    @NotEmpty(message = "电话不能为空")
    private String userinfoMobile;

    //状态暂时不考虑
    private Short userinfoStatus;

    @NotEmpty(message = "角色id不能为空")
    private String userinfoRoleid;

    private SystemRole systemRole;
    
    //setter和getters方法省略
}

3.7、编写修改用户信息界面updateUser.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en" >
<head>
    <meta charset="UTF-8">
    <title>updateUser</title>
</head>
<body>
    <form method="post" action="/doUpdateUser" id="userForm" style="margin: 100px auto;margin-left: 400px">
        <input type="hidden" name="userinfoUid" th:value="${systemUserinfo.userinfoUid}"/>
        登录名:<input type="text" id="userinfoLoginid" name="userinfoLoginid" th:value="${systemUserinfo.userinfoLoginid}" /><span th:text="${userinfoLoginid}"/><br/>
        姓名:<input type="text" id="userinfoName" name="userinfoName" th:value="${systemUserinfo.userinfoName}" /><span th:text="${userinfoName}"/><br/>
        密码:<input type="password" id="userinfoPassword" name="userinfoPassword" th:value="${systemUserinfo.userinfoPassword}"/><span th:text="${userinfoPassword}"/><br/>
        性别:<input type="radio" name="userinfoSex" th:checked="${systemUserinfo.userinfoSex == ''}"  value=""/><input type="radio" name="userinfoSex" th:checked="${systemUserinfo.userinfoSex == ''}" value=""/><span th:text="${userinfoSex}"/><br/>
        邮箱:<input type="text" id="userinfoEmail" name="userinfoEmail" th:value="${systemUserinfo.userinfoEmail}" /><span th:text="${userinfoEmail}"/><br/>
        电话:<input type="text" id="userinfoMobile" name="userinfoMobile" th:value="${systemUserinfo.userinfoMobile}"/><span th:text="${roleId}"/><span th:text="${userinfoMobile}"/><br/>
        角色:<select name="userinfoRoleid">
                <option value="">--请选择--</option>
                <option th:selected="${systemUserinfo.userinfoRoleid == role.roleId}" th:each="role:${roleList}" th:text="${role.roleName}" th:value="${role.roleId}" ></option>
             </select>
             <span th:text="${userinfoRoleid}"/><br/>

        <input type="submit" value="提交修改"/>
    </form>

<br/>

</body>
</html>

在这里插入图片描述

3.8、编写控制层SystemUserinfoController(修改用户信息控制方法)

@GetMapping("/toUpdateUser")
    public String toUpdateUser(String userinfoUid,HttpServletRequest request){

        List<SystemRole> roleList = systemRoleService.selectAll();
        SystemUserinfo systemUserinfo = systemUserinfoService.selectByPrimaryKey(userinfoUid);
        if(systemUserinfo != null && roleList != null){
            request.setAttribute("systemUserinfo",systemUserinfo);
            request.setAttribute("roleList",roleList);
        }
        return "updateUser";
    }

    @PostMapping("/doUpdateUser")
    public String doUpdateUser(@Valid  SystemUserinfo systemUserinfo,BindingResult bindingResult,HttpServletRequest request){

        //当前是否是错误
        if(bindingResult.hasErrors()) {
            List<SystemRole> roleList = systemRoleService.selectAll();
            request.setAttribute("roleList",roleList);
            //获取所有错误信息
            List<ObjectError> listError = bindingResult.getAllErrors();
            for (ObjectError objectError : listError) {
                FieldError fieldError = (FieldError) objectError;
                //校验字段 : 校验信息
                System.out.println(fieldError.getField() + "\t" + objectError.getDefaultMessage());
                //request作用一样实现将字段和错误信息存入request作用域中
                request.setAttribute(fieldError.getField(),objectError.getDefaultMessage());
            }
            return "updateUser";
        }else {
            int r = systemUserinfoService.updateByPrimaryKey(systemUserinfo);
            if(r>0){
                return "redirect:/selectAll";
            }else {
                request.setAttribute("msg","更新失败!请重新填写修改数据!");
                return "updateUser";
            }
        }
    }

3.9、修改用户信息校验

在这里插入图片描述
查看控制台:
在这里插入图片描述

3.10、回头再次测试登录校验

什么都不输直接提交:
在这里插入图片描述
当我们输入正确的用户名和密码时,再次提交:
在这里插入图片描述
查看控制台信息:
在这里插入图片描述
出现上述情况其实也是意料之中的事,当我们对应一个功能无论是登录还是修改、添加,只需要校验各自需要的字段时,其他字段自然也用不到,就不需要进行验证了,那我们怎么解决上述的问题呢?下述我们就引入注解分组的概念,使某个功能需要哪几个字段就对这几个字段进行分组。

3.11、对实体类SystemUserinfo中的字段进行分组

第一步先创建几个分组需要用到的空接口类:

//LoginGroup:
package com.kdcrm.util;

public interface LoginGroup {
}

//UpdateGroup:
package com.kdcrm.util;

public interface UpdateGroup {
}

//AddGroup:
package com.kdcrm.util;

public interface AddGroup {
}

第二步对实体类中的字段注解分类:

package com.kdcrm.pojo;

import com.kdcrm.util.AddGroup;
import com.kdcrm.util.LoginGroup;
import com.kdcrm.util.UpdateGroup;

import javax.validation.constraints.NotEmpty;
import java.io.Serializable;

public class SystemUserinfo implements Serializable {

    private String userinfoUid;

   @NotEmpty(message = "用户名不能为空",groups = {LoginGroup.class, UpdateGroup.class, AddGroup.class})
    private String userinfoLoginid;

    @NotEmpty(message = "名字不能为空",groups = { UpdateGroup.class, AddGroup.class})
    private String userinfoName;

    @NotEmpty(message = "登录密码不能为空",groups = {LoginGroup.class, UpdateGroup.class, AddGroup.class})
    private String userinfoPassword;

    @NotEmpty(message = "性别不能为空",groups = {UpdateGroup.class, AddGroup.class})
    private String userinfoSex;

    @NotEmpty(message = "邮件不能为空",groups = {UpdateGroup.class, AddGroup.class})
    private String userinfoEmail;

    @NotEmpty(message = "手机号不能为空",groups = {UpdateGroup.class, AddGroup.class})
    private String userinfoMobile;

    private Short userinfoStatus;

    @NotEmpty(message = "角色不能为空",groups = {UpdateGroup.class, AddGroup.class})
    private String userinfoRoleid;

    private SystemRole systemRole;

    //setter和getters方法省略
}

在这里插入图片描述

3.12、对控制层SystemUserinfoController中的方法进行(需求字段)分组


    @RequestMapping("/doLogin")
    public ModelAndView doLogin(@Validated(value = {LoginGroup.class}) SystemUserinfo systemUserinfo, BindingResult bindingResult, HttpServletRequest request, ModelAndView mav){

     	 //逻辑内容不变与上述一致,此处为了节省空间省略
    }

	@PostMapping("/doAddUser")
    public String doAddUser(@Validated(value = {AddGroup.class}) SystemUserinfo systemUserinfo,BindingResult bindingResult, HttpServletRequest request){
		
		//逻辑内容不变与上述一致,此处为了节省空间省略
    }
    
	@PostMapping("/doUpdateUser")
    public String doUpdateUser(@Validated(value = {UpdateGroup.class}) SystemUserinfo systemUserinfo, BindingResult bindingResult, HttpServletRequest request){

       //逻辑内容不变与上述一致,此处为了节省空间省略
    }

在这里插入图片描述

3.13、再再次测试登录界面:

在这里插入图片描述
查看控制台:
在这里插入图片描述
输入正确的用户名和密码:
在这里插入图片描述
容易吗,不容易,欢迎一起撒花!!!

4、写在最后

每一篇文章的总结其实都是需要耗费不少时间和精力的,我们同是选择编程这条路的人,那么就要从始至终拥有享受孤独的勇气,这个精彩的世界,每一分每一秒都在瞬息万变,我们生来平庸,想要改变,眼下便是努力的最好时刻,永远不要臣服于现实,因为这个世界真的很美好,需要我们不断的去创造和发现它的美好,一起加油,陌生人。

我还是相信那句话:在我们的指尖有改变世界的力量!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一宿君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值