SpringBoot系列之表单参数校验整理
Posted smileNicky
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot系列之表单参数校验整理相关的知识,希望对你有一定的参考价值。
1、前言
表单的校验在一些对接的接口,要求比较多,使用较多的是Hibernate的表单校验进行JSR-303验证,在springboot项目中,有封装的spring-boot-starter-validation这个starter,也是基于Hibernate的表单校验,下面通过一个RestFul风格的接口来看看
2、环境搭建
开发环境
- JDK 1.8
- SpringBoot2.2.1
- MybatisPlus3.4.3.4
- Maven 3.2+
- mysql5.7.36
- 开发工具
- IntelliJ IDEA
- smartGit
使用阿里云提供的脚手架快速创建项目:
https://start.aliyun.com/bootstrap.html
也可以在idea里,将这个链接复制到Spring Initializr这里,然后创建项目
jdk版本选择jdk8
加上必要的pom配置
表单校验选择Validation
也可以创建一个springboot项目之后,自己在pom配置文件加上配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
3、RestFul例子
下面以一个用户注册的例子,写一些RestFul的api接口
package com.example.validated.model.dto;
import com.example.validated.common.validated.EnumValueValidator;
import com.example.validated.model.enumCls.SexTypeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.io.Serializable;
@Data
@SuperBuilder(toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class UserDto implements Serializable
private Long id;
@NotBlank( message = "用户名name必须传!")
private String name;
private Integer age;
@Email( message = "email invalid!")
@NotBlank(message = "邮箱email必须传!")
private String email;
@NotBlank( message = "手机号contactNumber必须传")
@Pattern(regexp = "1[3|4|5|7|8][0-9]\\\\d8" ,message = "手机号格式不对")
private String contactNumber;
@NotBlank(message = "密码password必须传!")
@Size(min = 6 ,message = "密码password必须6位以上")
private String password;
@EnumValueValidator(enumClass = SexTypeEnum.class , enumMethod = "isValueValid" ,message = "性别类型校验有误!")
private Integer sex;
启动校验的一般有两种方法:
@Valid
加在请求正文之前,可以加在@RequestBody
注解之前@Validated
是JSR-303 的@Valid的变体,可以支持组校验,什么是组校验?
@PostMapping(value = "/user")
public ResultBean<User> save(@Valid @RequestBody UserDto userDto)
User user = BeanUtil.copyProperties(userDto , User.class);
boolean flag = userService.save(user);
if (flag) return ResultBean.ok(user);
return ResultBean.badRequest("新增失败");
在postman调用测试接口:
默认返回的异常信息:
"timestamp": "2022-01-12T06:37:45.076+0000",
"status": 400,
"error": "Bad Request",
"errors": [
"codes": [
"NotBlank.userDto.name",
"NotBlank.name",
"NotBlank.java.lang.String",
"NotBlank"
],
"arguments": [
"codes": [
"userDto.name",
"name"
],
"arguments": null,
"defaultMessage": "name",
"code": "name"
],
"defaultMessage": "姓名必须传!",
"objectName": "userDto",
"field": "name",
"rejectedValue": "",
"bindingFailure": false,
"code": "NotBlank"
],
"message": "Validation failed for object='userDto'. Error count: 1",
"path": "/api/user"
所以可以自己定义一个全局的异常类,捕获MethodArgumentNotValidException
异常信息:
package com.example.validated.common.exception;
import com.example.validated.common.rest.ResultBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.List;
import java.util.stream.Collectors;
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public ResultBean<?> exception(Exception e)
log.error("服务器错误," , e);
return ResultBean.serverError(e.getMessage());
@ResponseBody
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResultBean<?> exception(MethodArgumentNotValidException e)
log.error("参数校验异常," , e.getMessage());
List<String> res = e.getBindingResult().getAllErrors().stream()
.map(err -> String.format("%s->%s", ((FieldError) err).getField() , err.getDefaultMessage()))
.collect(Collectors.toList());
return ResultBean.badRequest("参数校验异常" , res);
自定义的ResultBean类:
package com.example.validated.common.rest;
import lombok.Data;
import org.springframework.http.HttpStatus;
@Data
public class ResultBean<T>
/**
* 状态
* */
private int status;
/**
* 描述
* */
private String desc;
/**
* 数据返回
* */
private T data;
public ResultBean(int status, String desc, T data)
this.status = status;
this.desc = desc;
this.data = data;
public ResultBean(T data)
this.status = HttpStatus.OK.value();
this.desc = "处理成功";
this.data = data;
public static <T> ResultBean<T> ok(T data)
return new ResultBean(data);
public static <T> ResultBean<T> ok()
return new ResultBean(null);
public static <T> ResultBean<T> badRequest(String desc,T data)
return new ResultBean(HttpStatus.BAD_REQUEST.value(), desc, data);
public static <T> ResultBean<T> badRequest(String desc)
return new ResultBean(HttpStatus.BAD_REQUEST.value(), desc, null);
public static <T> ResultBean serverError(String desc, T data)
return new ResultBean(HttpStatus.INTERNAL_SERVER_ERROR.value(),"服务器内部异常:"+desc,data);
public static <T> ResultBean serverError(String desc)
return new ResultBean(HttpStatus.INTERNAL_SERVER_ERROR.value(),"服务器内部异常:"+desc,null);
不自定义ResultBean类,可以加到ResponseEntity类:
@ResponseBody
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers, HttpStatus status, WebRequest request)
Map<String, Object> body = new HashMap<>(16);
body.put("timestamp", new Date());
body.put("status", status.value());
List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(err -> String.format("%s->%s", ((FieldError) err).getField() , err.getDefaultMessage()))
.collect(Collectors.toList());
body.put("errors", errors);
return new ResponseEntity<>(body, headers, status);
4、@Valid
和@Validated
区别
在 Spring 中,我们喜欢使用 JSR-303 的@Valid注解进行校验,同时还有@Validated
这个注解,两个注解看起来差不多,然后具体有什么差别?现在对比一下
- 注解地方
@Validated
:可以用在类型、方法和方法参数上,但是不能用在成员属性上@Valid
:可以用在方法、构造函数、方法参数和成员属性
综上其实最明显的区别还是能不能用在成员属性上,@Validated
是不支持的,在IDEA里加上去试试,马上会变红提示
- 分组校验
组校验,@Valid
是不支持的,但是可以使用@Validated
实现,举个场景,假如一个接口要进行分阶段同步数据,第一步同步基本的信息,第二步再同步附加信息,但是@Valid
是不支持这种分组校验的,下面例子参考https://www.baeldung.com/spring-valid-vs-validated
新增一个基础信息的接口
package com.example.validated.interfaces
// copy from https://github.com/eugenp/tutorials/blob/master/spring-boot-modules/spring-boot-mvc-3/src/main/java/com/baeldung/springvalidation/interfaces/BasicInfo.java
public interface BasicInfo
// validation group marker interface
所有用户信息接口
package com.example.validated.interfaces
public interface UserInfo
// validation group marker interface
原来的DTO类,加上groups 进行分组
package com.example.validated.model.dto;
import com.example.validated.common.validated.EnumValueValidator;
import com.example.validated.interfaces.BasicInfo;
import com.example.validated.interfaces.UserInfo;
import com.example.validated.model.enumCls.SexTypeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.io.Serializable;
@Data
@SuperBuilder(toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class UserGroupDto implements Serializable
private Long id;
@NotBlank( message = "用户名name必须传!" ,groups = BasicInfo.class , UserInfo.class)
private String name;
@NotBlank(message = "密码password必须传!")
@Size(min = 6 ,message = "密码password必须6位以上" ,groups = BasicInfo.class , UserInfo.class)
private String password;
private Integer age;
@Email( message = "email invalid!",groups = UserInfo.class)
@NotBlank(message = "邮箱email必须传!" ,groups = UserInfo.class)
private String email;
@NotBlank( message = "手机号contactNumber必须传" ,groups = UserInfo.class)
@Pattern(regexp = "1[3|4|5|7|8][0-9]\\\\d8" ,message = "手机号格式不对",groups = UserInfo.class)
private String contactNumber;
@EnumValueValidator(enumClass = SexTypeEnum.class , enumMethod = "isValueValid" ,message = "性别类型校验有误!",groups = UserInfo.class)
private Integer sex;
@Validated
只指定BasicInfo这个组的校验,也只校验用户名和密码,其它的不校验,@Validated(BasicInfo.class)
@PostMapping(value = "/user/basic")
public ResultBean<User> saveBasic(@Validated(BasicInfo.class) @RequestBody UserGroupDto userDto)
User user = BeanUtil.copyProperties(userDto , User<以上是关于SpringBoot系列之表单参数校验整理的主要内容,如果未能解决你的问题,请参考以下文章