Skip to content

为什么要全局异常处理类?

全局处理(Global Exception Handling)是一种集中管理异常和错误响应的机制。通过全局处理,可以统一处理应用中抛出的异常,避免在每个控制器中重复编写异常处理代码。

以下是全局处理的必要性:

1. 统一异常处理
    在大型应用中,异常可能分散在各个控制器中。如果没有全局处理机制,每个控制器都需要单独处理异常,导致代码重复和维护困难。
2. 提高代码可读性
    将异常处理逻辑集中在一个地方,可以使业务逻辑更加清晰,减少控制器中的冗余代码。
3. 标准化错误响应
    通过全局处理,可以确保所有异常都返回统一的错误响应格式,便于前端或其他客户端处理。
4. 增强安全性
    全局处理可以捕获未处理的异常,避免敏感信息泄露(如堆栈跟踪),提升应用的安全性。
5. 简化测试
    集中处理异常逻辑后,测试异常处理变得更加简单,无需在每个控制器中单独测试异常处理逻辑。

全局异常处理优势是什么?

  1. 减少代码重复

    通过全局处理,可以避免在每个控制器中重复编写异常处理代码,减少代码冗余。

  2. 统一错误响应格式

    全局处理可以确保所有异常都返回统一的错误响应格式,例如:

    js
    {
    "timestamp": "2023-10-01T12:00:00Z",
    "status": 400,
    "error": "Bad Request",
    "message": "Invalid input data",
    "path": "/api/users"
    }
  3. 提高开发效率

    开发者无需在每个控制器中单独处理异常,可以专注于业务逻辑的实现,提升开发效率。

  4. 增强应用健壮性

    全局处理可以捕获未处理的异常,避免应用崩溃,提升应用的健壮性。

  5. 支持自定义异常

    通过全局处理,可以轻松定义和处理自定义异常,满足特定业务需求。

如何实现全局异常处理拦截?

xiaomayi-common/xiaomayi-core 核心类库中已自定义了全局异常处理类:

js
package com.xiaomayi.core.exception;

import com.xiaomayi.core.utils.R;
import com.xiaomayi.core.utils.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import java.util.Objects;
import java.util.stream.Collectors;


/**
 * <p>
 * 全局异常处理类
 * </p>
 *
 * @author 小蚂蚁云团队
 * @since 2024-05-21
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 业务异常处理类
     *
     * @param e       异常处理
     * @param request 网络请求
     * @return 返回结果
     */
    @ExceptionHandler(BizException.class)
    public R handleServiceException(BizException e, HttpServletRequest request) {
        log.error(e.getMessage(), e);
        Integer code = e.getCode();
        return StringUtils.isNotNull(code) ? R.failed(code, e.getMessage()) : R.failed(e.getMessage());
    }

    /**
     * 非法请求异常处理
     *
     * @param e       异常处理
     * @param request 网络请求
     * @return 返回结果
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
        return R.failed(e.getMessage());
    }

    /**
     * 请求地址缺少参数
     *
     * @param e       异常处理
     * @param request 网络请求
     * @return 返回结果
     */
    @ExceptionHandler(MissingPathVariableException.class)
    public R handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e);
        return R.failed(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
    }

    /**
     * 请求参数类型不匹配
     *
     * @param e       异常处理
     * @param request 网络请求
     * @return 返回结果
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public R handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e);
        return R.failed(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), Objects.requireNonNull(e.getRequiredType()).getName(), e.getValue()));
    }

    /**
     * Spring封装的参数验证异常处理
     * 备注:作用于 @Validated @Valid 注解,接收参数加上@RequestBody注解(json格式)才会有这种异常
     *
     * @param e 异常处理
     * @return 返回结果
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error(e.getMessage(), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return R.failed(message);
    }

    /**
     * 参数绑定、验证异常处理
     * 备注:作用于@Validated @Valid 注解,仅对于表单提交有效,对于以json格式提交将会失效
     * 处理Get请求中 使用@Valid 验证路径中请求实体校验失败后抛出的异常
     *
     * @param e 异常处理
     * @return 返回结果
     */
    @ExceptionHandler(BindException.class)
    public R handleBindException(BindException e) {
        log.error(e.getMessage(), e);
        String message = e.getAllErrors().get(0).getDefaultMessage();
        return R.failed(message, HttpStatus.BAD_REQUEST.value());
    }

    /**
     * 处理请求参数格式错误 @RequestParam上validate失败后抛出的异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public R ConstraintViolationExceptionHandler(ConstraintViolationException e) {
        String message = e.getConstraintViolations().stream().map(v -> v.getMessage()).collect(Collectors.joining());
        return R.failed(message);
    }

    /**
     * 上传文件大小验证未通过异常处理
     *
     * @param e 异常处理
     * @return 返回结果
     */
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public R handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
        log.error("上传文件验证异常:{}", e.getMessage());
        return R.failed("上传文件大小超过限制");
    }

}

总结

在项目中,全局处理的必要性和好处包括:

统一异常处理:集中管理异常逻辑,减少代码重复。
标准化错误响应:确保所有异常返回统一的响应格式。
提高开发效率:减少重复代码,专注于业务逻辑。
增强应用健壮性:捕获未处理异常,避免应用崩溃。
支持自定义异常:灵活处理特定业务异常。

通过合理使用全局处理机制,可以显著提升代码质量、开发效率和系统健壮性。

小蚂蚁云团队 · 提供技术支持

小蚂蚁云 新品首发
新品首发,限时特惠,抢购从速! 全场95折
赋能开发者,助理企业发展,提供全方位数据中台解决方案。
获取官方授权