51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

springboot全局异常捕获

# springboot 全局异常捕获 {#springboot-全局异常捕获}

本文讲述 springboot 项目如何支持全局异常捕获。系统的很多处代码逻辑都可能出现异常,但是每处都用 try catch 捕获,不仅冗余代码过多,也影响代码的美观,更不利于维护。若不进行异常捕获,springboot 会自动将异常信息吐到响应数据中,对用户非常不友好。那么,可以通过全局异常捕获,来避免上述问题。
提示

本文介绍要捕获的异常是指接口处理逻辑中发生的异常,如定时任务等处发生的异常不会被捕获,因为定时任务的逻辑并非通过接口触发。

若不对系统做全局异常捕获,那么有如下缺点:

  1. 接口返回的信息不友好
  2. 接口出现异常时, 响应的数据并不会以接口定义的统一数据格式返回, 不利于前端对接

在未做全局异常捕获时, 异常接口返回的数据格式如下:

{
  "timestamp": "2020-09-11 15:34:29",
  "status": 400,
  "error": "Bad Request",
  "errors": [
    {
      "codes": [
        "NotBlank.addFeedbackDto.content",
        "NotBlank.content",
        "NotBlank.java.lang.String",
        "NotBlank"
      ],
      "arguments": [
        {
          "codes": [
            "addFeedbackDto.content",
            "content"
          ],
          "arguments": null,
          "defaultMessage": "content",
          "code": "content"
        }
      ],
      "defaultMessage": "内容不能为空",
      "objectName": "addFeedbackDto",
      "field": "content",
      "rejectedValue": "",
      "bindingFailure": false,
      "code": "NotBlank"
    }
  ],
  "message": "Validation failed for object='addFeedbackDto'. Error count: 1",
  "path": "/api/user/feedback/addFeedback"
}

既然如此,那么我们需要为系统定义全局异常捕获处理器

# 1. 创建全局异常捕获处理器 {#_1-创建全局异常捕获处理器}

实现全局异常捕获非常简单, 只需要定义如下一个类即可

package com.ruiboyun.facehr.exception;

import cn.hutool.core.exceptions.ExceptionUtil; import com.ruiboyun.facehr.common.entity.Result; import lombok.extern.log4j.Log4j2; import org.springframework.validation.BindException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ValidationException; import java.util.StringJoiner;

/**

  • 全局异常处理器 / @RestControllerAdvice @Log4j2 public class GlobalExceptionHandler { /*

    • 参数绑定错误
    • @param ex
    • @return */ @ExceptionHandler(BindException.class) public Result handleBindException(BindException ex) { StringJoiner sj = new StringJoiner(";"); ex.getBindingResult().getFieldErrors().forEach(x -> sj.add(x.getDefaultMessage())); log.error("参数绑定错误, error[{}]", ExceptionUtil.stacktraceToOneLineString(ex)); return Result.error(sj.toString()); }

    /**

    • 参数校验错误
    • @param ex
    • @return */ @ExceptionHandler(ValidationException.class) public Result handleValidationException(ValidationException ex) { log.error("参数校验错误, error[{}]", ExceptionUtil.stacktraceToOneLineString(ex)); return Result.error(ex.getCause().getMessage()); }

    /**

    • 字段校验不通过异常
    • @param ex
    • @return */ @ExceptionHandler(MethodArgumentNotValidException.class) public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) { StringJoiner sj = new StringJoiner(";"); ex.getBindingResult().getFieldErrors().forEach(x -> sj.add(x.getDefaultMessage())); log.error("字段校验不通过异常, error[{}]", ExceptionUtil.stacktraceToOneLineString(ex)); return Result.error(sj.toString()); }

    /**

    • Controller参数绑定错误
    • @param ex
    • @return */ @ExceptionHandler(MissingServletRequestParameterException.class) public Result handleMissingServletRequestParameterException(MissingServletRequestParameterException ex) { log.error("Controller参数绑定错误, error[{}]", ExceptionUtil.stacktraceToOneLineString(ex)); return Result.error(ex.getMessage()); }

    /**

    • 处理方法不支持异常
    • @param ex
    • @return */ @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class) public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) { log.error("处理方法不支持异常, error[{}]", ExceptionUtil.stacktraceToOneLineString(ex)); return Result.error("HTTP方法错误"); }

    /**

    • 其他未知异常
    • @param ex
    • @return */ @ExceptionHandler(value = Exception.class) public Result handleException(Exception ex) { log.error("其他未知异常, error[{}]", ExceptionUtil.stacktraceToOneLineString(ex)); return Result.error("未知异常"); } }

提示

@RestControllerAdvice 注解相当于@ControllerAdvice + @ResponseBody,简化了注解的数量

那么,现在系统已经具备全局异常捕获的功能。现在访问任何一个接口, 无论接口是否出现异常, 都会返回统一的数据格式,且友好的提示异常信息。

{
  "code": 1,
  "msg": "内容不能为空",
  "data": null
}

# 2. 自定义异常 {#_2-自定义异常}

您可以自定义异常类, 实现在系统逻辑的任何一个位置抛出自定义异常, 交给全局异常处理器。   若想让全局异常处理器捕获自定义异常, 那么需要仿照上面的例子补充一个用来捕获自定义异常的方法。

赞(3)
未经允许不得转载:工具盒子 » springboot全局异常捕获