SpringBoot参数校验及异常捕获

原文链接:https://blog.csdn.net/ROAOR1/article/details/114118542

参数校验的简单实现
创建一个springboot项目,引入校验依赖依赖(版本可以更换)

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
注意:这里@Validated需要打在类上,开启方法验证

@Validated
@RestController
@RequestMapping("/validate")
public class ValidateController {

@GetMapping("/run")
public void run(@NotBlank(message = "body不能为空") String body){
System.out.println(body);
}
}
1
2
3
4
5
6
7
8
9
10
这样一个最基本的校验就结束了

但是会发现去调用接口的时候,提示的是系统内部错误,这种情况下就不利于前后端联调,明明是少传的参数,结果提示的确实系统内部错误,这个时候就需要加一个全局异常处理器,特定处理参数校验的异常,将我们想要的异常信息返回给前端


全局异常处理器
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

/**
* get请求中的参数校验
* @param exception
* @return
*/
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public CommonResult ConstraintViolationExceptionHandler(ConstraintViolationException exception){
log.error("参数异常信息 :", exception);
StringBuffer message = new StringBuffer();
exception.getConstraintViolations().forEach(e -> {
message.append(e.getMessage()).append(",");
});
String result = message.toString();
return CommonResult.fail(result.substring(0, result.length() - 1));
}

/**
* form-data格式的参数校验
* @param exception
* @return
*/
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public CommonResult bindExceptionHandler(BindException exception){
log.error("参数异常信息 :", exception);
StringBuffer message = new StringBuffer();
exception.getAllErrors().forEach(error -> {
message.append(error.getDefaultMessage()).append(",");
});
String result = message.toString();
return CommonResult.fail(result.substring(0, result.length() - 1));
}

/**
* json格式的参数校验
* @param exception
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public CommonResult handle(MethodArgumentNotValidException exception) {
log.error("参数异常信息:", exception);
StringBuffer message=new StringBuffer();
exception.getBindingResult().getAllErrors().forEach((error) -> {
String errorMessage = error.getDefaultMessage();
message.append(errorMessage).append(",");
});
String result = message.toString();
return CommonResult.fail(result.substring(0, result.length() - 1));
}

/**
* 兜底的一个异常处理
* @param exception
* @return
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public CommonResult excepitonHandler(Exception exception) {
log.error("服务器异常 :", exception);
return CommonResult.fail("服务器开小差了,请稍后重试!");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
这里我添加了三种参数校验的异常处理,上面的例子就会被ConstraintViolationExceptionHandler这个处理器拦截到,进而处理成我们想要的格式返回给前端


@Getter
@Setter
public class BabyInfoIn {

private Integer id;

@NotBlank(message = "用户名不能为空")
private String userName;
}
1
2
3
4
5
6
7
8
9
添加两个方法用来触发另外两种异常,@Validated必须加在方法上,否则不会生效

/**
* form-data格式请求,触发BindException异常
* @param babyInfo
*/
@PostMapping("/run2")
public void run2(@Validated BabyInfoIn babyInfoIn){
System.out.println("参数校验成功");
}

/**
* json格式请求,触发MethodArgumentNotValidException异常
* 必须有babyInfoIn这个对象时才会触发,为null时不会触发
* @param babyInfo
*/
@PostMapping("/run3")
public void run3(@Validated @RequestBody BabyInfoIn babyInfoIn){
System.out.println("参数校验成功");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
我这里使用的通用返回

@Getter
@Setter
public class CommonResult<T> {

private Integer status;

private String msg;

private T date;

private CommonResult(T date){
this.status = CommonEnum.SUCCESS.getStatus();
this.msg = CommonEnum.SUCCESS.getMsg();
this.date = date;
}

private CommonResult(Integer status, String msg, T date){
this.status = status;
this.msg = msg;
this.date = date;
}

public static <T> CommonResult<T> ok(){
return new CommonResult<>(null);
}

public static <T> CommonResult<T> ok(T date){
return new CommonResult<>(date);
}

public static <T> CommonResult<T> fail(String msg){
return new CommonResult<>(CommonEnum.FAIL.getStatus(), msg, null);
}

public static <T> CommonResult<T> fail(String msg, T date){
return new CommonResult<>(CommonEnum.FAIL.getStatus(), msg, date);
}

public static <T> CommonResult<T> fail(Integer status,String msg, T date){
return new CommonResult<>(status, msg, date);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
简单的返回枚举

public enum CommonEnum {
SUCCESS(1,"成功"),
FAIL(-1, "失败");
private Integer status;

private String msg;

CommonEnum(Integer status, String msg) {
this.status = status;
this.msg = msg;
}

public Integer getStatus() {
return status;
}

public String getMsg() {
return msg;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
参数校验的常用注解


嵌套的参数校验
在BabyInfoIn实体中,添加BabyInfo 实体,并添加@Valid注解
注意:@Validated不支持嵌套校验

@Valid
private BabyInfo babyInfo;
1
2
@Getter
@Setter
public class BabyInfo implements Serializable {

private static final long serialVersionUID = 1L;

private Integer babyId;

@NotNull(message = "性别不能为空")
private Integer gender;
}
1
2
3
4
5
6
7
8
9
10
11
接着还是调用上面的run3方法,会发现嵌套异常校验生效


参数校验分组
在实际开发中,分组的概念是必不可少的,分组就是创建一个普通的接口,以接口实现区分

public @interface Create {
}
1
2
public @interface Select {
}
1
2
具体的实现也很简单,以run3方法为例,就是在@Validated中加上属性用于区分

@PostMapping("/run3")
public void run3(@Validated(Select.class) @RequestBody BabyInfoIn babyInfoIn){
System.out.println("参数校验成功");
}
1
2
3
4
如果不写groups 属性,那就是default默认分组

@NotBlank(message = "用户名不能为空",groups = Select.class)
private String userName;

//create和select都需要校验该参数
@Length(min = 0, max = 50, message = "描叙需在0-50字符之间", groups = {Select.class, Create.class})
private String desc;
1
2
3
4
5
6
Validate配置
@Configuration
public class ValidateConfig {
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
//failFast设置为true表示只要出现校验失败的情况,就立即结束校验,不再进行后续的校验。
.failFast(true)
.buildValidatorFactory();
return validatorFactory.getValidator();
}

@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
methodValidationPostProcessor.setValidator(validator());
return methodValidationPostProcessor;
}
}

posted @ 2022-03-02 08:57  枫树湾河桥  阅读(1382)  评论(0编辑  收藏  举报
Live2D