使用@RestControllerAdvice全局异常处理—捕获自定义注解校验接口入参抛出的异常

一、需求背景

https://mp.csdn.net/mp_blog/creation/editor/140054265

上期,我们提到,使用自定义注解的方式实现接口入参校验,并抛出异常;

这期,我们对抛出的异常进行全局捕获处理,统一处理接口异常返回。

二、实现方案

        @RestControllerAdvice是Spring框架提供的一个注解,用于定义全局异常处理器和全局数据绑定设置。它结合了@ControllerAdvice和@ResponseBody两个注解的功能。

        @ControllerAdvice是一个用于定义全局控制器增强(即全局异常处理和全局数据绑定)的注解通过使用@ControllerAdvice,我们可以将异常处理和数据绑定逻辑集中到一个类中,避免在每个控制器中重复编写相同的异常处理代码。

        @ResponseBody是用于指示控制器方法返回的对象将被直接写入响应体中的注解。

        @RestControllerAdvice注解标记的类将被视为全局异常处理器,并且异常处理方法的返回值将以JSON格式直接写入响应体中。

        通过在@RestControllerAdvice类中定义异常处理方法,我们可以捕获和处理控制器中抛出的异常,提供自定义的异常处理逻辑,以及返回适当的响应给客户端。

这样可以统一处理应用程序中的异常情况,提高代码的可维护性和可读性。

三、编码实现

3.1 自定义异常

package com.saferycom.validate;

/**
 * @author LH
 */
public class LhException  extends RuntimeException{
    String [] params;

    public LhException() {
    }
    public LhException(String errCode) {
        super(errCode);
    }
    public LhException(String errCode, String[] params) {
        super(errCode);
        this.params = params;
    }
}

3.2 controller层代码,抛出自定义注解校验出的异常

package com.saferycom.validate;

import cn.hutool.core.collection.CollectionUtil;
import com.saferycom.domain.AjaxResult;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.util.Set;

/**
 * @author LH
 */
@RestController
@RequestMapping("/test")
public class ValidateTestController {

    @Resource
    private Validator validator;

    @PostMapping("/validate")
    public AjaxResult validateTest(@RequestBody RegisterAddress address) {
        // 校验
        Set<ConstraintViolation<RegisterAddress>> violations = validator.validate(address);
        // 遍历,抛出异常
        if (CollectionUtil.isNotEmpty(violations)) {
            violations.forEach(l -> {
                throw new LhException(l.getMessage());
            });
        }
        // 正常返回
        return AjaxResult.success();
    }
}

 3.3 定义一个全局异常处理类,捕获各种类型的异常

根据处理的异常类型,可以根据实际需要新增 

package com.saferycom.global;

import com.saferycom.domain.AjaxResult;
import com.saferycom.validate.LhException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;


/**
 * 全局异常处理
 *
 * @author LH
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(LhException.class)
    public AjaxResult handleLhException(LhException e) {
        // 返回报错信息
        return AjaxResult.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
    }
}

3.4 自定义注解部分代码

 自定义注解

package com.saferycom.validate;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

/**
 * @author LH
 */
@Documented
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PccMatchValidator.class)
public @interface PccMatch {

    // 省
    String prov();
    // 市
    String city();
    // 区
    String country();

    // 默认错误消息
    String message() default "Invalid value for MyConstraint";

    // 分组
    Class<?>[] groups() default {};

    // 负载
    Class<? extends Payload>[] payload() default {};

}

自定义注解校验器

package com.saferycom.validate;

import org.springframework.beans.BeanWrapperImpl;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

/**
 * @author LH
 */
public class PccMatchValidator implements ConstraintValidator<PccMatch, Object> {
    private String prov;
    private String city;
    private String country;

    @Override
    public void initialize(PccMatch args) {
        // 初始化逻辑
        this.prov = args.prov();
        this.city = args.city();
        this.country = args.country();
    }


    @Override
    public boolean isValid(Object obj, ConstraintValidatorContext context) {
        // 结构体为空的默认返回true
        if (obj == null) {
            return true;
        }

        BeanWrapperImpl beanWrapper = new BeanWrapperImpl(obj);
        String province = (String) beanWrapper.getPropertyValue(this.prov);
        String city1 = (String) beanWrapper.getPropertyValue(this.city);
        String country1 = (String) beanWrapper.getPropertyValue(this.country);

        // todo 这里可以添加具体的验证逻辑,这里简单示例
        if ("31000".equals(province) && "31100".equals(city1) && "31101".equals(country1)) {
            return true;
        }

        // 根据需要,重新设置报错message
        context.disableDefaultConstraintViolation();
        context.buildConstraintViolationWithTemplate("省-市-区不匹配").addConstraintViolation();

        return false;
    }
}

3.5 启动服务测试返回

 由此可见,被@RestControllerAdvice注解修饰的全局异常处理类,捕获了自定义注解校验抛出的异常,并将异常信息响应出去。

四、总结

全局的异常处理器,提供自定义的异常处理,返回适当的响应给客户端。统一处理应用程序的异常,提高代码的可维护性和可读性。

 @RestControllerAdvice的使用还可以指定对应的注解、包,类。比如你需要返回自定义结果格式,可以指定@RestController层使用,如果你要指定类和包也可以。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值