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-自定义异常}

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

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