SpringBoot Validation优雅的全局参数校验

28 篇文章 0 订阅
24 篇文章 0 订阅

前言【参考资料】

我们都知道在平时写controller时候,都需要对请求参数进行后端校验,一般我们可能会这样写

public String add(UserVO userVO) {
    if(userVO.getAge() == null){
        return "年龄不能为空";
    }
    if(userVO.getAge() > 120){
        return "年龄不能超过120";
    }
    if(userVO.getName().isEmpty()){
        return "用户名不能为空";
    }
    // 省略一堆参数校验...
    return "OK";
}

业务代码还没开始写呢,光参数校验就写了一堆判断。这样写虽然没什么错,但是给人的感觉就是:不优雅,不专业,代码可读性也很差,一看就是新手写的代码

作为久经战争的老司机怎么能这样呢,大神是不允许这样代码出现的,其实SpringBoot提供整合了参数校验解决方案 spring-boot-starter-validation

整合使用【参考资料】

  • 在SpringBootv2.3之前的版本只需要引入 web
    依赖就可以了他包含了validation校验包在此之后SpringBoot版本就独立出来了需要单独引入依赖
<!--参数校验-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
内置的校验注解有很多,罗列如下:

注解 校验功能
@AssertFalse 必须是false
@AssertTrue 必须是true
@DecimalMax 小于等于给定的值
@DecimalMin 大于等于给定的值
@Digits 可设定最大整数位数和最大小数位数
@Email 校验是否符合Email格式
@Future 必须是将来的时间
@FutureOrPresent 当前或将来时间
@Max 最大值
@Min 最小值
@Negative 负数(不包括0)
@NegativeOrZero 负数或0
@NotBlank 不为null并且包含至少一个非空白字符
@NotEmpty 不为null并且不为空
@NotNull 不为null
@Null 为null
@Past 必须是过去的时间
@PastOrPresent 必须是过去的时间,包含现在
@Pattern 必须满足正则表达式
@PositiveOrZero 正数或0
@Size 校验容器的元素个数

单个参数校验
使用很简单只需要在需要校验controller上加上 @Validated 注解在需校验参数上加上 @NotNull,@NotEmpty 之类参数校验注解就行了,

@Validated
@GetMapping("/home")
public class ProductController {
  public Result index(@NotBlank String name, @Email @NotBlank String email) {
        return ResultResponse.success();
    }
}

对象参数校验
在上面的基础上只需要在对象参数前面加上 @Validated 注解,然后在需要校验的对象参数的属性上面加上
@NotNull,@NotEmpty 之类参数校验注解就行了,【参考资料】

@PostMapping("/user")
public Result index1(@Validated @RequestBody UserParams userParams) {
        log.info("info  test######");
        log.error("error test #####");
        return ResultResponse.success(userParams);
    }
@Data
public class UserParams {

    @NotBlank
    private String username;
    private int age;
    @NotBlank
    private String addr;
    @Email
    private String email;



}

参数校验异常信息处理

上面我们进行了参数校验,默认当参数校验没通过后会通过异常方式来抛出错误信息 MethodArgumentNotValidException 是在校验时抛出的还包括很多其他异常信息,这时我们可以通过全局异常捕获信息来处理这些参数校验异常【参考资料】

全局异常处理类只需要在类上标注@RestControllerAdvice,并在处理相应异常的方法上使用@ExceptionHandler注解,写明处理哪个异常即可

package cn.soboys.core;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.soboys.core.authentication.AuthenticationException;
import cn.soboys.core.ret.Result;
import cn.soboys.core.ret.ResultCode;
import cn.soboys.core.ret.ResultResponse;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Path;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/17 20:19
 * 全局异常统一处理
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理 json 请求体调用接口对象参数校验失败抛出的异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result jsonParamsException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        List errorList = CollectionUtil.newArrayList();

        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            String msg = String.format("%s%s;", fieldError.getField(), fieldError.getDefaultMessage());
            errorList.add(msg);
        }
        return ResultResponse.failure(ResultCode.PARAMS_IS_INVALID, errorList);
    }


    /**
     * 处理单个参数校验失败抛出的异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result ParamsException(ConstraintViolationException e) {

        List errorList = CollectionUtil.newArrayList();
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        for (ConstraintViolation<?> violation : violations) {
            StringBuilder message = new StringBuilder();
            Path path = violation.getPropertyPath();
            String[] pathArr = StrUtil.splitToArray(path.toString(), ".");
            String msg = message.append(pathArr[1]).append(violation.getMessage()).toString();
            errorList.add(msg);
        }
        return ResultResponse.failure(ResultCode.PARAMS_IS_INVALID, errorList);
    }

    /**
     * @param e
     * @return 处理 form data方式调用接口对象参数校验失败抛出的异常
     */
    @ExceptionHandler(BindException.class)
    public Result formDaraParamsException(BindException e) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        List<String> collect = fieldErrors.stream()
                .map(o -> o.getField() + o.getDefaultMessage())
                .collect(Collectors.toList());
        return ResultResponse.failure(ResultCode.PARAMS_IS_INVALID, collect);
    }

    /**
     * 请求方法不被允许异常
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        return ResultResponse.failure(ResultCode.METHOD_NOT_ALLOWED);
    }

    /**
     * @param e
     * @return Content-Type/Accept 异常
     * application/json
     * application/x-www-form-urlencoded
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Result httpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
        return ResultResponse.failure(ResultCode.BAD_REQUEST);
    }

    /**
     * handlerMapping  接口不存在跑出异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public Result noHandlerFoundException(NoHandlerFoundException e) {
        return ResultResponse.failure(ResultCode.NOT_FOUND, e.getMessage());
    }


    /**
     * 认证异常
     * @param e
     * @return
     */
    @ExceptionHandler(AuthenticationException.class)
    public Result UnNoException(AuthenticationException e) {
        return ResultResponse.failure(ResultCode.UNAUTHORIZED,e.getMessage());
    }

    /**
     *
     * @param e 未知异常捕获
     * @return
     */
    @ExceptionHandler(Exception.class)
    public Result UnNoException(Exception e) {
        return ResultResponse.failure(ResultCode.INTERNAL_SERVER_ERROR, e.getMessage());
    }
}
  • 这里关于全局异常处理请参考我这篇 SpringBoot优雅的全局异常处理

  • 这里我返回的是自定义响应体api 请参考我这篇 Spring Boot 无侵入式 实现RESTful API接口统一JSON格式返回

当然校验异常的处理还有其它方式,当参数在没有通过校验情况下会帮我们把错误信息注入到BindingResult对象中,会注入到对应controller方法,这个仅限于参数有 @RequestBody 或者 @RequestParam 修饰的参数才行

public String add1(@Validated UserVO userVO, BindingResult result) {
    List<FieldError> fieldErrors = result.getFieldErrors();
    if(!fieldErrors.isEmpty()){
        return fieldErrors.get(0).getDefaultMessage();
    }
    return "OK";
}

当然我推荐第一种可以通过全局异常处理的方式统一处理校验异常

  • 如果每个Controller方法中都写一遍对BindingResult信息的处理,使用起来还是很繁琐。代码很冗余
  • 当我们写了@validated注解,不写BindingResult的时候,SpringBoot
    就会抛出异常。由此,可以写一个全局异常处理类来统一处理这种校验异常,从而免去重复组织异常信息的代码。

最后来自小编的福利

小编整理了一份大厂真题的面试资料,以及2021最新Java核心技术整理的资料集锦,需要领取的小伙伴可以 私聊关注我 免费领取 ,编程的世界永远向所有热爱编程的人开放,这是一个自由,平等,共享的世界,我始终是这样坚信的。
喜欢小编的分享可以点赞关注哦,小编持续为你分享最新文章 和 福利领取哦

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值