SpringBoot中参数校验的方法有哪些
这篇文章给大家分享的是有关SpringBoot中参数校验的方法有哪些的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。
一、前言
在 Web 开发中经常需要对前端传过来的参数进行校验,例如格式校验、非空校验等,基本上每个接口都需要进行校验。如果使用常规的 IF ELSE
进行校验,随着参数越来越多,校验逻辑的冗余度也越来越高,导致维护性变差。在 Java 中定义了一套基于注解的数据校验规范 Bean Validation ,通过一些简单的注解就能完成必要的逻辑校验,相对来说就方便了很多。而 Bean Validation 只是规范,并没有具体的实现,Hibernate 提供了具体的实现,也即 Hibernate Validator ,这个也是目前使用得比较多的验证器了。
二、注解介绍
validator 内置注解
@Null
被注释的元素必须为null
@NotNull
被注释的元素必须不为null
@AssertTrue
被注释的元素必须为true
@AssertFalse
被注释的元素必须为false
@Min(value)
被注释的元素必须是一个数字,其值必须大于等于指定的最小值@Max(value)
被注释的元素必须是一个数字,其值必须小于等于指定的最大值@DecimalMin(value)
被注释的元素必须是一个数字,其值必须大于等于指定的最小值@DecimalMax(value)
被注释的元素必须是一个数字,其值必须小于等于指定的最大值@Size(max, min)
被注释的元素的大小必须在指定的范围内@Digits (integer, fraction)
被注释的元素必须是一个数字,其值必须在可接受的范围内@Past
被注释的元素必须是一个过去的日期@Future
被注释的元素必须是一个将来的日期@Pattern(value)
被注释的元素必须符合指定的正则表达式
Hibernate Validator 附加的 constraint
@Email
被注释的元素必须是电子邮箱地址@Length
被注释的字符串的大小必须在指定的范围内@NotEmpty
被注释的字符串的必须非空@Range
被注释的元素必须在合适的范围内@NotBlank
验证字符串非null
,且长度必须大于0
注意
@NotNull
用于验证对象是否不为null
,无法检测长度为0的字符串;@NotEmpty
用于 String、Map 或者数组等集合类型不能为null
且长度必须大于0;@NotBlank
只能用于String,不能为null
,且调用trim()
后,长度必须大于0;
校验字符串是否为空,使用
@NotNull
,只有参数不传的时候才会检测到,传了空值(例如空字符串)仍然可以通过校验,因此应该使用@NotBlank
三、添加依赖
在 SpringBoot
中 Bean Validation 已经集成在 starter-web
中,因此无需再添加依赖。但是本人实际测试发现,直接使用好像不行,因此添加了如下依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
四、创建用于校验的实体类
创建一个 validator 目录,在里面创建一个 UserDTO
类:
本来想直接复用之前创建的 entity 类,但是后来想了下,entity 用于建立数据库的映射关系,字段跟数据表是一一对应的,而这里的 validator 是用于校验前端传过来的参数,字段跟前端传的参数是对应的,因此不能复用,需要单独写一个 validator 类。
顺便提一下,在 RestController 中使用自己定义的对象,需要有 setter、getter 之类的方法,或者使用 lombok 的 @Data 注解。如果不加的话会报错:
No converter found for return value of type: class validator.UserDTO
使用 getter、setter 方法如下:
public class UserDTO {private String username;private Integer age;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}}
使用 lombok 的 @Data
注解如下,代码与上面等效:
@Datapublic class UserDTO {private String username;private Integer age;}
五、写一个测试用的接口
添加一个 POST 接口,从请求体中获取参数,然后原封不动返回过去(主要是用来测试参数校验的,这里接口逻辑并不重要)
@PostMapping("validateUser")public UserDTO userValidate(@RequestBody UserDTO userDTO) { return userDTO;}
六、在实体类中添加注解
给需要校验的参数添加注解:
@Datapublic class UserDTO { @NotBlank(message = "用户名不能为空") private String username; @NotBlank(message = "手机号不能为空") private String mobile; @NotNull(message = "性别不能为空") private Integer sex; @NotNull(message = "年龄不能为空") private Integer age; @NotBlank(message = "邮箱不能为空") @Email(message = "邮箱格式错误") private String email;}
七、在 controller 方法中添加 Validated 注解
然后需要在 controller
方法体添加 @Validated
,不加 @Validated
校验会不起作用。
用下面的数据测试一下:
{ "username": "", "mobile": "2333", "sex": 0, "age": 0, "email": "233@dby.com"}
可以看到校验是成功了,但是后台抛了一个异常:
Validation failed for argument [0] in public validator.UserDTO com.hhlnyfz.hhlnyfz.HelloController.userValidate(validator.UserDTO): [Field error in object ‘userDTO' on field ‘username': rejected value []; codes [NotBlank.userDTO.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userDTO.username,username]; arguments []; default message [username]]; default message [用户名不能为空]] ]
然后返回参数并不理想,前端也并不容易处理返回参数:
八、添加全局异常处理
上面这种情况需要添加一下全局异常处理,这样比较规范。创建一个 GlobalExceptionHandler
类,在类的上面添加 @RestControllerAdvice
注解,然后添加如下代码:
@RestControllerAdvicepublic class GlobalExceptionHandler { // 捕获 MethodArgumentNotValidException 异常 @ExceptionHandler(value = MethodArgumentNotValidException.class) public HashMap<String, Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) { HashMap<String, Object> map = new HashMap<>(); map.put("code", 400); map.put("msg", e.getMessage()); map.put("url", request.getRequestURL()); return map; } // 其他异常处理方法}
这边 @ExceptionHandler
注解中的 MethodArgumentNotValidException.class
用于捕获请求参数异常。如果是 Exception.class
表示捕获全部异常。不要用一个方法处理所有的异常,而是一个方法处理一种异常。如果需要处理其他异常,可以在下面添加方法。
还是用刚才的测试用例,这次异常被捕获到了,返回的内容如下:
可以看到 e.getMessage()
把整个错误堆栈信息全部打印出来了,但我们只需要把最后的 default message
返回给前端就行,因此改用 e.getBindingResult().getFieldError().getDefaultMessage()
,然后 IDE 给了提示:
Method invocation ‘getDefaultMessage' may produce ‘NullPointerException'
也就是说 e.getBindingResult().getFieldError()
可能会是一个空指针 null
,于是按照 IDE 的提示用 Objects.requireNonNull
包裹一下,最终代码如下:
@RestControllerAdvicepublic class GlobalExceptionHandler { // 捕获 MethodArgumentNotValidException 异常 @ExceptionHandler(value = MethodArgumentNotValidException.class) public HashMap<String, Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) { HashMap<String, Object> map = new HashMap<>(); map.put("code", 400); map.put("msg", Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage()); map.put("url", request.getRequestURL()); return map; } // 其他异常处理方法}
响应内容如下:
当然这边还是有不规范的地方,没有用统一响应体进行返回,后面会介绍如何封装统一响应体。
九、分组校验
@Valid
和 @Validated
两个注解都可以实现校验,前面的功能用 @Valid
也是可以的,但是 @Validated
功能更强大,可以实现分组校验。什么是分组校验,分组校验实际上实现了实体类的复用,有时候并不希望对所有的参数都进行校验,例如下面这个情况:
@Datapublic class Route {@NotNull(message = "始发地省id不能为空")private Integer startProvinceId;@NotNull(message = "目的地省id不能为空")private Integer endProvinceId;@NotBlank(message = "详细地址不能为空")private String address;}
假如在一个接口中只希望校验 startProvinceId
和 address
,而在另一个接口中只希望校验 endProvinceId
和 address
,这个时候就可以用分组校验。可以定义一个接口:
public interface ValidateGroup {interface RouteValidStart {}interface RouteValidEnd {}}
然后在实体类中添加分组:
@Datapublic class Route {@NotNull(groups = {RouteValidStart.class}, message = "始发地省id不能为空")private Integer startProvinceId;@NotNull(groups = {RouteValidEnd.class}, message = "目的地省id不能为空")private Integer endProvinceId;@NotBlank(groups = {RouteValidStart.class, RouteValidEnd.class}, message = "详细地址不能为空")private String address;}
然后在校验的时候只需要把分组传入 @Validate
就可以实现指定参数的校验:
@RequestMapping("addRoute")public ServerResponse addRoute(@RequestBody @Validated({RouteValidStart.class}) Route route) {// ...return ServerResponse.success();}
十、单个参数校验
在参数前面加上注解即可:
@PostMapping("/get")public ReturnVO getUserInfo(@RequestParam("userId") @NotNull(message = "用户ID不能为空") String userId){ return new ReturnVO().success();}
然后在 Controller 类上面增加 @Validated 注解,注意不是增加在参数前面。
springboot是什么
springboot一种全新的编程规范,其设计目的是用来简化新Spring应用的初始搭建以及开发过程,SpringBoot也是一个服务于框架的框架,服务范围是简化配置文件。
感谢各位的阅读!关于“SpringBoot中参数校验的方法有哪些”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341