• 微服务项目:尚融宝(10)(后端接口:统一异常处理)


    认清现实,放弃幻想,准备斗争

    一、项目中的异常

    1、制造异常

    屏蔽 IntegralGrade 中的 @TableField注解

    1. @ApiModelProperty(value = "逻辑删除(1:已删除,0:未删除)")
    2. //@TableField("is_deleted")
    3. @TableLogic
    4. private Boolean deleted;

    2、Swagger中测试

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

    二、统一异常处理

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

    1、创建统一异常处理器

    guigu-common中创建exception包,创建统一异常处理器类UnifiedExceptionHandler

    1. @Slf4j
    2. @Component //Spring容易自动管理
    3. @RestControllerAdvice //在controller层添加通知。如果使用@ControllerAdvice,则方法上需要添加@ResponseBody
    4. public class UnifiedExceptionHandler {
    5. /**
    6. * 未定义异常
    7. */
    8. @ExceptionHandler(value = Exception.class) //当controller中抛出Exception,则捕获
    9. public R handleException(Exception e) {
    10. log.error(e.getMessage(), e);
    11. return R.error();
    12. }
    13. }

    2、service-core添加扫描

    添加 "com.atguigu.common"

    1. @SpringBootApplication
    2. @ComponentScan({"com.atguigu.srb", "com.atguigu.common"})
    3. public class ServiceCoreApplication {

    3、测试

    返回统一错误结果

    三、处理特定异常

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

    1、添加依赖

    在guigu-common中添加jdbc依赖

    1. org.springframework
    2. spring-jdbc

    2、添加异常处理方法

    在 UnifiedExceptionHandler 中添加

    1. /**
    2. * 特定异常
    3. */
    4. @ExceptionHandler(BadSqlGrammarException.class)
    5. public R handleBadSqlGrammarException(BadSqlGrammarException e){
    6. log.error(e.getMessage(), e);
    7. return R.setResult(ResponseEnum.BAD_SQL_GRAMMAR_ERROR);
    8. }

    3、测试

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

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

    4、恢复制造的异常

    @TableField(value = "is_deleted")

    四、自定义异常

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

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

    1、创建自定义异常类

    1. @Data
    2. @NoArgsConstructor
    3. public class BusinessException extends RuntimeException {
    4. //状态码
    5. private Integer code;
    6. //错误消息
    7. private String message;
    8. /**
    9. *
    10. * @param message 错误消息
    11. */
    12. public BusinessException(String message) {
    13. this.message = message;
    14. }
    15. /**
    16. *
    17. * @param message 错误消息
    18. * @param code 错误码
    19. */
    20. public BusinessException(String message, Integer code) {
    21. this.message = message;
    22. this.code = code;
    23. }
    24. /**
    25. *
    26. * @param message 错误消息
    27. * @param code 错误码
    28. * @param cause 原始异常对象
    29. */
    30. public BusinessException(String message, Integer code, Throwable cause) {
    31. super(cause);
    32. this.message = message;
    33. this.code = code;
    34. }
    35. /**
    36. *
    37. * @param resultCodeEnum 接收枚举类型
    38. */
    39. public BusinessException(ResponseEnum resultCodeEnum) {
    40. this.message = resultCodeEnum.getMessage();
    41. this.code = resultCodeEnum.getCode();
    42. }
    43. /**
    44. *
    45. * @param resultCodeEnum 接收枚举类型
    46. * @param cause 原始异常对象
    47. */
    48. public BusinessException(ResponseEnum resultCodeEnum, Throwable cause) {
    49. super(cause);
    50. this.message = resultCodeEnum.getMessage();
    51. this.code = resultCodeEnum.getCode();
    52. }
    53. }

    2、添加异常处理方法

    UnifiedExceptionHandler类中添加

    1. /**
    2. * 自定义异常
    3. */
    4. @ExceptionHandler(BusinessException.class)
    5. public R handleBusinessException(BusinessException e){
    6. log.error(e.getMessage(), e);
    7. return R.error().message(e.getMessage()).code(e.getCode());
    8. }

    3、修改Controller

    在AdminIntegralGradeController的方法中添加异常处理,业务中需要的位置抛出BusinessException自定义异常。
    1. @ApiOperation("新增积分等级")
    2. @PostMapping("/save")
    3. public R save(
    4. @ApiParam(value = "积分等级对象", required = true)
    5. @RequestBody IntegralGrade integralGrade){
    6. //如果借款额度为空就手动抛出一个自定义的异常!
    7. if(integralGrade.getBorrowAmount() == null){
    8. //BORROW_AMOUNT_NULL_ERROR(-201, "借款额度不能为空"),
    9. throw new BusinessException(ResponseEnum.BORROW_AMOUNT_NULL_ERROR);
    10. }
    11. boolean result = integrationService.save(integralGrade);
    12. if (result) {
    13. return R.ok().message("保存成功");
    14. } else {
    15. return R.error().message("保存失败");
    16. }
    17. }

    4、测试

    测试

     结果

    五、异常处理优化

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

     1、什么是断言

    1. public class AssertTests {
    2. //if else的用法
    3. @Test
    4. public void test1() {
    5. Object o = null;
    6. if (o == null) {
    7. throw new IllegalArgumentException("用户不存在.");
    8. }
    9. }
    10. //断言的用法:更为简洁
    11. @Test
    12. public void test2() {
    13. // 另一种写法
    14. Object o = null;
    15. Assert.notNull(o, "用户不存在.");
    16. }
    17. }

    2、自定义断言

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

    1. @Slf4j
    2. public abstract class Assert {
    3. /**
    4. * 断言对象不为空
    5. * 如果对象obj为空,则抛出异常
    6. * @param obj 待判断对象
    7. */
    8. public static void notNull(Object obj, ResponseEnum responseEnum) {
    9. if (obj == null) {
    10. log.info("obj is null...............");
    11. throw new BusinessException(responseEnum);
    12. }
    13. }
    14. }

    3、修改controller

    在controller中用断言替换if else

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

    六、Controller上层异常

    1、异常分类

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

    2、处理Controller上层异常

    UnifiedExceptionHandler中添加

    1. /**
    2. * Controller上一层相关异常
    3. */
    4. @ExceptionHandler({
    5. NoHandlerFoundException.class,
    6. HttpRequestMethodNotSupportedException.class,
    7. HttpMediaTypeNotSupportedException.class,
    8. MissingPathVariableException.class,
    9. MissingServletRequestParameterException.class,
    10. TypeMismatchException.class,
    11. HttpMessageNotReadableException.class,
    12. HttpMessageNotWritableException.class,
    13. MethodArgumentNotValidException.class,
    14. HttpMediaTypeNotAcceptableException.class,
    15. ServletRequestBindingException.class,
    16. ConversionNotSupportedException.class,
    17. MissingServletRequestPartException.class,
    18. AsyncRequestTimeoutException.class
    19. })
    20. public R handleServletException(Exception e) {
    21. log.error(e.getMessage(), e);
    22. //SERVLET_ERROR(-102, "servlet请求异常"),
    23. return R.error().message(ResponseEnum.SERVLET_ERROR.getMessage()).code(ResponseEnum.SERVLET_ERROR.getCode());
    24. }

    3、测试

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

  • 相关阅读:
    centos安装nginx(root操作)
    Python画图之散点图(plt.scatter)
    算法基础知识总结
    数据库连接池
    Microsoft Learn: Docker入门教程
    17.1 隐藏执行CMD命令
    自动化API测试工具ReadyAPI新增业务历史记录功能
    放下宝宝就醒,告诉你是因为什么
    优秀开源云原生工具推荐——系列4
    如何在你的 wordpress 网站中添加搜索框
  • 原文地址:https://blog.csdn.net/m0_62436868/article/details/126616506