1.数据校验该谁做?
当我们随便打开一个网站注册页面的时候,用户名和密码等等属性往往会伴随着数据校验的工作,
身为后端开学学习的小白,我们应该知道数据校验这个工作该谁做?
1.前端工程师需要在页面通过JavaScript代码等等来设置数据校验
2.后端开发也需要通过Java代码等等来设置数据校验
2.数据校验小案例
打开百度的注册页面,当我们输入用户名过长的时候,页面上会提示我们"用户名不能超过7个汉字或者14个字符"
设想一下,如果这个数据校验工作由后端开发来实现流程大概是这样的:
1.前端界面输入框由用户输入信息
2.通过点击别的区域由JavaScript代码来接受这个事件
3.JavaScript将接收到的事件信息通过ajax,axios等等发给后端
4.后端接受数据,进行校验,然后传递给前端
仅仅是这么一个小功能,就耗费大量的流程时间显然是不合理的。
不妨再设想一下,如果这个数据校验仅仅由前端开发来实现流程大概是这样的:
1.前端页面输入框由用户输入信息
2.通过点击事件或者别的事件获取到信息
3.前端进行数据校验并显示
听起来好像没有什么问题,而且流程也简化了很多
但是作为开发无论是前端还有后端,我们都不能简单的将用户想象成好人,更多的应该是想象为居心叵测的坏人,如果他们使用python爬虫,postman工具这么一些程序或者软件来绕过前端js代码数据验证,那前端没有一点办法。
所以呢数据校验这个工作应该由前后端协同来开发,从而应对不同的业务场景。
3.validation验证框架
1 关于Spring Validation
一种方式是如果我们仅仅凭人力来实现数据的校验,不合法可以抛出异常,但是工作量和代码复杂度会比较高 ,而且很有可能会因为疏忽遗漏等等造成一些本不该出现的bug或者缺失的
第二种方式就是采用市场上主流的 Spring Validation
框架去实现校验,所以 Spring Validation
框架的主要作用是 检查参数的基本有效性。
2 使用流程
在Spring Boot工程中,使用此框架需要添加依赖,并刷新maven
<!-- Spring Boot支持Spring Validation的依赖项,用于检查参数的基本有效性 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
springboot内置了validation框架的,可以通过IDEA在项目初始化的时候直接勾选
3 快速入门
1.在处理请求的方法的参数列表中,在POJO类型的参数上添加@Validated
注解,表示需要通过Spring Validation框架检查此参数,例如UserController中注册功能:
2.在此POJO类中的属性上,添加对应的检查注解,以配置检查规则,
例如,添加@NotNull
注解,就表示“不允许为null
”的规则!
在UserDTO
类
此时应该加的注解都加了,该做的事情也都做完,但是仔细想一想其实疏忽了一个大问题,
如果数据校验框架validation校验完毕了,该怎么办?是直接报错?还是抛出异常?还是返回一些什么东西?
3.重启工程,在Knife4j中测试,当提交请求时,如果username参数为 null
,服务器端将响应400
错误。
同时终端也出现了异常, MethodArgumentNotValidException:方法参数无效的异常
如果业务逻辑简单一些,验证的流程到这里其实就已经结束了,只需要处理一下异常然后由前端加一些页面来为用户显示
具体的流程请点击下面的链接,里面有详细的教程
Spring MVC中异常处理_springmvc全局异常处理-优快云博客
但是平常的数据校验不会这么简单,所以我们仍然需要掌握更多的数据校验注解
4.明确提示消息
当提交的username
的值为 null 时,可以发现异常已被处理!
但是,处理结果并不合适,因为,客户端得到此结果后,仍无法明确出现了什么错误!
所有的检查注解都可以配置message
参数,用于对错误进行描述。
-
第1步:
@NotNull
注解中添加message
参数@NotNull(message = "必须提交用户名") private String username;
-
第2步:自定义枚举状态码 StatusCode
VALIDATE_ERROR(3002, "参数校验失败")
-
第3步:异常方法中获取提示信息 message
在处理异常时,需要调用
MethodArgumentNotValidException
对象的getFieldError().getDefaultMessage()
获取以上配置的描述文本@ExceptionHandler public JsonResult handleBindException(MethodArgumentNotValidException ex){ /* ex.getFieldError().getDefaultMessage():获取 @NotNull(message="xxx") 中message的消息 */ String message = ex.getFieldError().getDefaultMessage(); return new JsonResult(StatusCode.VALIDATE_ERROR, message); }
-
第4步:重启工程,在Knife4j中测试
5 常用注解
5.1 @NotNull
注解
-
作用:用于验证对象是否为 null
-
用法:
@NotNull
注解用于对象类型上 -
示例
@NotNull(message = "用户名不能为null") private String username;
5.2 @NotEmpty
注解
-
作用:用于验证字符串是否为空,并且会检查是否为 null 值(为null值时报错)
-
用法:用于字符串类型上
-
示例
@NotEmpty(message = "用户名不能为空") private String username;
5.3 @NotBlank
注解
-
作用:不允许为空白,即不允许是“仅由空格、TAB等空白值组成的字符串”,也不允许为空字符串,也不允许为空值null
-
用法:用于字符串类型上
-
示例
@NotBlank(message = "用户名不能为空白串") private String username;
5.4 @Size
注解
-
作用:可以指定最小值和最大值限制字符串的长度
-
用法:用于字符串类型参数
-
示例
@Size(min = 6, max = 20, message = "用户名长度必须在6到20之间") private String username;
5.5 @Range
注解
-
作用:用于验证数字类型字段的取值范围,通过配置min和max属性来限制数值类型参数的值区间包括最小值和最大值
-
用法:用于数值类型参数
-
示例
@Range(min = 1, max = 10, message = "年龄必须在1-10岁之间") private int age; @Range(min = 0.1, max = 1.0, message = "成绩必须在0.1到1.0之间") private double score;
6 非POJO参数校验
在 Spring Validation 中,除了对 POJO(Plain Old Java Object)进行校验的功能外,还支持对非 POJO 进行校验,比如 String、Integer、Double 等类型的参数。
6.1 使用流程
-
在当前方法所在的类上添加
@Validated
注解 -
在参数上添加对应的检查注解
6.2 使用示例
对于微博详情页的 id 参数进行范围校验,范围只能在1-10之间
-
第1步:在类
WeiboController
中添加@Validated
注解@Validated public class WeiboController {}
-
第2步:在控制器方法参数
id
上添加对应的检查注解 -
public JsonResult selectById( (@Range(min = 1 , max = 500 , message = "查找微博id必须在1-500之间") @RequestParam("id") Long id )
-
第3步:重启工程,在Knife4j或者浏览器中测试
1.@RequestParam注解
在这里的@RequestParam注解,
官方给出的解释:
给出的解释很抽象,我们可以这样通俗的解释:
后端开发的时候我们会根据url来进行请求访问后端服务器,比如
http://localhost:8080/v1/users/weibo?id=1
这里的@RequestParam就是用来接收请求的参数id的,可以在@RequestParam("id")注解上加上参数("id")这就代表了前端输入的地址,?后面必须是id=1的格式,如果参@RequestParam("abc")那就说明,地址?后面的格式必须是abc=1等等才能正确接收
那我之前也没有加过这个注解呀?不是照样可以正确接收吗?
它还是能正常拿到 ?id=1
的参数。
这是因为:
Spring Boot 会根据参数名自动去请求参数中匹配同名的字段,也就是 "id"。
但这种用法有几个问题:
-
不明确,不够语义化(看代码不知道参数从哪来的)
-
一旦参数名和多个来源都能匹配,就会出现歧义,比如路径参数、表单参数等
-
Swagger、Knife4j 等工具无法解析这个参数来自哪儿,所以文档不会显示参数定义
我在这里把该注解去掉后,打开knife4j文档,发现没有Lond id这个请求参数了
所以我们使用@RequestParam注解,功能之一便是搭配第三方的一些工具比如knife4j,来解决spring boot和别的工具框架的冲突。
更准确的说法是:
@RequestParam 是让 Spring 明确知道你这个参数来自 URL 的查询参数,同时也能让文档生成工具正确识别参数来源、类型、描述
2.@Parm注解
@Param
是 MyBatis 框架 提供的,用于:
在 Mapper 接口中为多个参数命名,方便在 SQL 映射文件中引用,MyBatis 在 Mapper 接口中,如果你没有使用
@Param
显式命名参数,它会自动使用param1
、param2
、param3
……这样的名字来表示位置参数。。
public interface UserMapper {
UserVO selectUser(
@Param("username") String username,
@Param("password") String password);
}
你在对应的 UserMapper.xml
中这样写 SQL:
<select id="selectUser" resultType="com.xxx.UserVO">
SELECT * FROM users
WHERE username = #{username} AND password = #{password}
</select>
因为你用了 @Param("username")
,所以在 XML 中你就可以直接用 #{username}
,否则 MyBatis 不知道你传入的参数叫什么。
如果不写 @Param
会怎么样?
-
如果方法里 只有一个参数,可以不写,MyBatis 会自动用
param1
来识别。 -
如果方法有 多个参数而你不写 @Param,那你在 XML 中只能用
param1
、param2
来引用:
WHERE username = #{param1} AND password = #{param2}
可读性很差,而且代码体验也很差。
在下面我将@Param去掉了,在UserMapper.xml中将#{}中内容换为 默认的param1,
param2
,通过knife4j测试,仍然可以登录成功