7. 尚融宝统一处理异常

一、项目中的异常

1、制造异常

屏蔽 IntegralGrade.java 中的 @TableField注解

7. 尚融宝统一处理异常_第1张图片

2、Swagger中测试

测试列表查询功能,查看结果,发生错误,显示响应失败

7. 尚融宝统一处理异常_第2张图片

二、统一异常处理

目标:我们想让异常结果也显示为统一的返回结果对象,并且统一处理系统的异常信息,那么需要进行统一异常处理。

1、创建统一异常处理器

indi-common模块中添加exception

创建统一异常处理器类UnifiedExceptionHandler.java

package com.indi.common.exception;

@Slf4j
@RestControllerAdvice
public class UnifiedException {
     
    // 捕获所有异常
    @ExceptionHandler(value = Exception.class)
    public R handleException(Exception e){
     
        log.error(e.getMessage(),e);
        return R.error();
    }
}

2、service-core添加扫描

ServiceCoreApplication.java

添加 “com.indi.common”

7. 尚融宝统一处理异常_第3张图片

3、测试

返回统一错误结果

7. 尚融宝统一处理异常_第4张图片

三、处理特定异常

如果我们不想显示统一的“服务器内部错误”,需要个性化的显示异常信息,那么需要针对特定的异常做处理

1、添加依赖

indi-common的pom中添加jdbc依赖

		<dependency>
			<groupId>org.springframeworkgroupId>
			<artifactId>spring-jdbcartifactId>
		dependency>

2、添加异常处理方法

UnifiedExceptionHandler.java 中添加

    @ExceptionHandler(value = BadSqlGrammarException.class)
    public R handleBadSqlGrammarException(BadSqlGrammarException e){
     
        log.error(e.getMessage(),e);
        return R.setResult(ResponseEnum.BAD_SQL_GRAMMAR_ERROR);
    }

3、测试

7. 尚融宝统一处理异常_第5张图片

问题:上面的例子虽然针对特定的异常显示个性化的错误信息,但是你会发现,针对每个不同的异常我们都需要在项目中添加对应的处理方法,并捕获对应的异常对象,可能还要针对这个异常添加额外的依赖。这显然不是最好的方式。

方案:此类异常直接抛出,并且用Exception类捕获就可以了。

4、实体类恢复原样

7. 尚融宝统一处理异常_第6张图片

四、自定义异常

上面两种方案,第一种不灵活,提示过于单调,第二种较为繁琐,有很多异常的话,需要写很多的方法。

目标:使用一个或较少的异常类,可以捕获和显示所有的异常信息。

方案:因此,我们可以创建一个自定义异常类(必须是运行时异常),在程序中抛出这个自定义异常对象,并在统一异常处理器中捕获自定义异常对象

1、创建自定义异常类

indi-common模块中创建BusinessException.java

package com.indi.common.exception;

@Data
@NoArgsConstructor
public class BusinessException extends RuntimeException {
     
    private Integer code;
    private String message;

    /**
     * @param message 错误消息
     */
    public BusinessException(String message) {
     
        this.message = message;
    }

    /**
     * @param message 错误消息
     * @param code    错误码
     */
    public BusinessException(String message, Integer code) {
     
        this.message = message;
        this.code = code;
    }

    /**
     * @param message 错误消息
     * @param code    错误码
     * @param cause   原始异常对象
     */
    public BusinessException(String message, Integer code, Throwable cause) {
     
        super(cause);
        this.message = message;
        this.code = code;
    }

    /**
     * @param resultCodeEnum 接收枚举类型
     */
    public BusinessException(ResponseEnum resultCodeEnum) {
     
        this.message = resultCodeEnum.getMessage();
        this.code = resultCodeEnum.getCode();
    }

    /**
     * @param resultCodeEnum 接收枚举类型
     * @param cause          原始异常对象
     */
    public BusinessException(ResponseEnum resultCodeEnum, Throwable cause) {
     
        super(cause);
        this.message = resultCodeEnum.getMessage();
        this.code = resultCodeEnum.getCode();
    }
}

2、添加异常处理方法

UnifiedExceptionHandler.java类中添加

    @ExceptionHandler(BusinessException.class)
    public R handleBusinessException(BusinessException e){
     
        log.error(e.getMessage(),e);
        return R.error().setCode(e.getCode()).setMessage(e.getMessage());
    }

3、修改Controller

AdminIntegralGradeController.java的方法中添加异常处理,业务中需要的位置抛出BusinessException自定义异常。

        if (integralGrade.getBorrowAmount()==null) {
     
            throw new BusinessException(ResponseEnum.BORROW_AMOUNT_NULL_ERROR);
        }

7. 尚融宝统一处理异常_第7张图片

4、测试

测试

7. 尚融宝统一处理异常_第8张图片

结果

7. 尚融宝统一处理异常_第9张图片

五、异常处理优化

目标:以优雅的 Assert(断言) 方式来校验业务的异常情况,消除 if else

1、什么是断言

package com.indi.srb.core;

import org.junit.Test;
import org.springframework.util.Assert;

public class AssertTests {
     

    @Test
    public void test1(){
     
        Object o = null;
        if (o == null) {
     
            throw new IllegalArgumentException("参数错误");
        }
    }

    @Test
    public void test2(){
     
        Object o = null;
        Assert.notNull(o,"参数错误");
    }
}

2、自定义断言

用断言的方式封装异常的抛出

indi-common模块创建Assert.java

package com.indi.common.exception;

@Slf4j
public class Assert {
     
    /**
     * 断言对象不为空,为空则抛异常
     *
     * @param obj
     * @param responseEnum
     */
    public static void notNull(Object obj, ResponseEnum responseEnum){
     
        if (obj == null) {
     
            log.info("obj is null...............................");
            throw new BusinessException(responseEnum);
        }
    }

    /**
     * 断言对象为空
     * 如果对象obj不为空,则抛出异常
     * @param object
     * @param responseEnum
     */
    public static void isNull(Object object, ResponseEnum responseEnum) {
     
        if (object != null) {
     
            log.info("obj is not null......");
            throw new BusinessException(responseEnum);
        }
    }

    /**
     * 断言表达式为真
     * 如果不为真,则抛出异常
     *
     * @param expression 是否成功
     */
    public static void isTrue(boolean expression, ResponseEnum responseEnum) {
     
        if (!expression) {
     
            log.info("fail...............");
            throw new BusinessException(responseEnum);
        }
    }

    /**
     * 断言两个对象不相等
     * 如果相等,则抛出异常
     * @param m1
     * @param m2
     * @param responseEnum
     */
    public static void notEquals(Object m1, Object m2,  ResponseEnum responseEnum) {
     
        if (m1.equals(m2)) {
     
            log.info("equals...............");
            throw new BusinessException(responseEnum);
        }
    }

    /**
     * 断言两个对象相等
     * 如果不相等,则抛出异常
     * @param m1
     * @param m2
     * @param responseEnum
     */
    public static void equals(Object m1, Object m2,  ResponseEnum responseEnum) {
     
        if (!m1.equals(m2)) {
     
            log.info("not equals...............");
            throw new BusinessException(responseEnum);
        }
    }

    /**
     * 断言参数不为空
     * 如果为空,则抛出异常
     * @param s
     * @param responseEnum
     */
    public static void notEmpty(String s, ResponseEnum responseEnum) {
     
        if (StringUtils.isEmpty(s)) {
     
            log.info("is empty...............");
            throw new BusinessException(responseEnum);
        }
    }
}

3、修改controller

AdminIntegralGradeController.java中用断言替换if else

7. 尚融宝统一处理异常_第10张图片

        Assert.notNull(integralGrade.getBorrowAmount(), ResponseEnum.BORROW_AMOUNT_NULL_ERROR);

六、Controller上层异常

1、异常分类

对异常按阶段进行分类,大体可以分成:进入Controller前的异常 和 业务层异常,具体可以参考下图:

7. 尚融宝统一处理异常_第11张图片

2、处理Controller上层异常

UnifiedExceptionHandler.java中添加

    /**
     * Controller上一层的相关异常
     */  
    @ExceptionHandler({
     
            NoHandlerFoundException.class,
            HttpRequestMethodNotSupportedException.class,
            HttpMediaTypeNotSupportedException.class,
            MissingPathVariableException.class,
            MissingServletRequestParameterException.class,
            TypeMismatchException.class,
            HttpMessageNotReadableException.class,
            HttpMessageNotWritableException.class,
            MethodArgumentNotValidException.class,
            HttpMediaTypeNotAcceptableException.class,
            ServletRequestBindingException.class,
            ConversionNotSupportedException.class,
            MissingServletRequestPartException.class,
            AsyncRequestTimeoutException.class
    })
    public R handleServletException(Exception e) {
     
        log.error(e.getMessage(), e);
        // SERVLET_ERROR(-102, "servlet请求异常"),
        return R.error().setCode(ResponseEnum.SERVLET_ERROR.getCode()).setMessage(ResponseEnum.SERVLET_ERROR.getMessage());
    }

3、测试

在save测试用例中输入非法的json参数,则得到下面的结果。我们可以在控制台日志中查看具体的错误原因。前端只需要返回相对简单友好的提示即可。

7. 尚融宝统一处理异常_第12张图片

你可能感兴趣的:(尚融宝,exception)