SpringBoot参数校验及全局异常处理

参数校验的简单实现

创建一个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>

注意:这里@Validated需要打在类上,开启方法验证

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

    @GetMapping("/run")
    public void run(@NotBlank(message = "body不能为空") String body){
        System.out.println(body);
    }
}

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

全局异常处理器

@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("服务器开小差了,请稍后重试!");
    }
}

这里我添加了三种参数校验的异常处理,上面的例子就会被ConstraintViolationExceptionHandler这个处理器拦截到,进而处理成我们想要的格式返回给前端
在这里插入图片描述

@Getter
@Setter
public class BabyInfoIn {

    private Integer id;

    @NotBlank(message = "用户名不能为空")
    private String userName;
}

添加两个方法用来触发另外两种异常,@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("参数校验成功");
}

我这里使用的通用返回

@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);
    }
}

简单的返回枚举

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;
    }
}

参数校验的常用注解

在这里插入图片描述

嵌套的参数校验

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

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

    private static final long serialVersionUID = 1L;

    private Integer babyId;

    @NotNull(message = "性别不能为空")
    private Integer gender;
}

接着还是调用上面的run3方法,会发现嵌套异常校验生效
在这里插入图片描述

参数校验分组

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

public @interface Create {
}
public @interface Select {
}

具体的实现也很简单,以run3方法为例,就是在@Validated中加上属性用于区分

@PostMapping("/run3")
    public void run3(@Validated(Select.class) @RequestBody BabyInfoIn babyInfoIn){
        System.out.println("参数校验成功");
    }

如果不写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;

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;
    }
}

参考

Validated 注解完成 Spring Boot 参数校验

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值