SpringBoot 全局异常处理和接口入参校验

本文介绍了在SpringBootWeb项目中如何避免冗余的异常处理代码,通过全局异常配置类和使用`@Validated`与`@Valid`注解进行参数校验,以及`@RestControllerAdvice`处理不同类型的异常情况。
摘要由CSDN通过智能技术生成

目录

问题

冗余代码写法

如何解决

示例代码

pom

公共结果类

全局异常配置类

Controller

DTO


问题

使用 spring boot 开发 web 项目时,基本上每一个接口都要做异常捕捉处理和入参校验,如果每个都硬编码在每个接口中,会导致程序中存在大量重复的代码。

冗余代码写法

    @RequestMapping("/valid")
    public ResponseEntity<UserDTO> valid(@RequestBody UserDTO dto){

        try {
            //校验入参
            Assert.notNull(dto.getName(), "name 不能为空");
            
            return ResponseEntity.ok(dto);
        } catch (Exception e) {
            //记录日志
            throw new RuntimeException(e);
        }
    }

如何解决

配置 spring 的全局异常处理(@RestControllerAdvice)

使用 validation 校验入参(@Validated 和 @Valid),@Validated 没法作用在类成员对象上,而 @Valid 可以,在实际业务中经常会遇到类成员变量为其他业务对象的情况,所以使用 @Valid 替代 @Validation

示例代码

pom

    <properties>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>

公共结果类

@Data
public class ResultVO<T> {

    private static String SUCCESS_CODE = "200000";
    private static String SUCCESS_MSG = "请求成功";
    private static String ERROR_CODE = "000000";
    private static String ERROR_MSG = "请求错误";

    private String code;
    private String msg;
    private T data;

    public ResultVO(String code, String message) {
        this.code = code;
        this.msg = message;
    }

    public ResultVO(String code, String message, T data) {
        this.code = code;
        this.msg = message;
        this.data = data;
    }

    public static <T> ResultVO<T> success() {
        return new ResultVO<>(SUCCESS_CODE, SUCCESS_MSG);
    }

    public static <T> ResultVO<T> success(String msg) {
        return new ResultVO<>(SUCCESS_CODE, msg);
    }

    public static <T> ResultVO<T> success(T data) {
        return new ResultVO<>(SUCCESS_CODE, SUCCESS_MSG, data);
    }

    public static <T> ResultVO<T> success(String code, String msg) {
        return new ResultVO<>(code, msg);
    }

    public static <T> ResultVO<T> success(String code, String msg, T data) {
        return new ResultVO<>(code, msg, data);
    }

    public static <T> ResultVO<T> error() {
        return new ResultVO<>(ERROR_CODE, ERROR_MSG);
    }

    public static <T> ResultVO<T> error(String msg) {
        return new ResultVO<>(ERROR_CODE, msg);
    }

    public static <T> ResultVO<T> error(T data) {
        return new ResultVO<>(ERROR_CODE, ERROR_MSG, data);
    }

    public static <T> ResultVO<T> error(String code, String msg) {
        return new ResultVO<>(code, msg);
    }

    public static <T> ResultVO<T> error(String code, String msg, T data) {
        return new ResultVO<>(code, msg, data);
    }
}

全局异常配置类

@Slf4j
@RestControllerAdvice
public class ExceptionConfiguration {

    /**
     * body json 校验异常捕获
     *
     * @param e 错误信息集合
     * @return ResultVO
     */
    @ExceptionHandler (MethodArgumentNotValidException.class)
    public ResultVO<?> validationBodyException(MethodArgumentNotValidException e) {
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        String message = allErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(joining(";"));
        log.error("捕获异常: {}", e);
        return ResultVO.error(message);
    }

    /**
     * form 校验异常捕获
     *
     * @param e  错误信息集合
     * @return ResultVO
     */
    @ExceptionHandler(value = BindException.class)
    public ResultVO<?> bindExceptionHandler(BindException e) {
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        String message = allErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(joining(";"));
        log.error("捕获异常: {}", e);
        return ResultVO.error(message);
    }

    /**
     * query param 校验异常捕获
     *
     * @param e  错误信息集合
     * @return ResultVO
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    public ResultVO<?> constraintViolationExceptionHandler(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        String message = constraintViolations.stream().map(ConstraintViolation::getMessage)
                        .collect(joining(","));
        log.error("捕获异常: {}", e);
        return ResultVO.error(message);
    }

    /**
     * 兜底异常处理
     * 
     * @param e 兜底异常
     * @return ResultVO
     */
    @ExceptionHandler (Exception.class)
    public ResultVO<?> runtimeException(Exception e) {
        log.error("捕获异常", e);
        return ResultVO.error(e.getMessage());
    }
}

Controller

@Slf4j
@RestController
@RequestMapping ("test")
public class TestController {

    @RequestMapping("/valid")
    public ResponseEntity<UserDTO> valid(@RequestBody @Valid UserDTO dto){
        return ResponseEntity.ok(dto);
    }

}

DTO

public class UserDTO {
    @NotNull(message = "用户id不能为空")
    private Integer id;

    @NotEmpty(message = "用户名不能为空")
    @Size(min = 4, max = 30, message = "用户名只能在4~30位之间")
    private String name;

    @NotEmpty(message = "密码不能为空")
    private String password;

    @Min(message = "年龄最小为18岁", value = 18)
    @Max(message = "年龄最大为80岁", value = 80)
    @NotNull(message = "年龄不能为空")
    private int age;

    @Pattern(regexp = "^1[35678]\\d{9}$", message = "手机号格式不正确")
    @NotBlank(message = "手机号不能为空")
    private String phone;

    @NotBlank(message = "邮箱不能为空")
    @Email(message = "请输入正确的邮箱")
    private String email;

    @Valid
    private InnerClass innerClass;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public InnerClass getInnerClass() {
        return innerClass;
    }

    public void setInnerClass(InnerClass innerClass) {
        this.innerClass = innerClass;
    }

    public static class InnerClass {
        @NotBlank(message = "innerField不能为空")
        private String innerField;

        public String getInnerField() {
            return innerField;
        }

        public void setInnerField(String innerField) {
            this.innerField = innerField;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值