SpringBoot 全局异常拦截捕获处理


SpringBoot 全局异常拦截捕获

一、为什么要定义全局异常捕获?

在写代码的时候,可能每个方法都会涉及到异常情况!

spring boot 默认情况下会映射到 /error 进行异常处理,但是提示并不十分友好!!!

通常展示到页面是这样的!!

code: 500 msg: “Failed to convert property value of type ‘java.lang.String’ to required type ‘java.lang.Integer’ for property ‘showId’; nested exception is java.lang.NumberFormatException: For input string: “eqwe””

或者是这样的页面
image-20201021221105929

那么这个时候呢…由于杂乱不堪的错误提示页面,用户看不懂,所以对用户来说呢,体验很差

在开发前后端一体的单体项目时候呢,为了让异常在页面显示上更好看一点,让用户体验性更好一点,我们通常可以自定义一些精巧可爱的异常页 例如 404页面 500页面 401 403 页面等等…

现在如火如荼的前后端分离模式正在每个公司进行,前端的请求到了后端后呢,通常后端是以json形式将数据返回给前端…这个时候,如果服务器内部出现异常,我们如何处理呢?或者因为前端传递的参数后端业务处理不同过,我们手动抛了异常,苛求之前的数据库操作进行回滚,如何对内部异常处理后响应给前端呢???

这个时候呢!就需要使用到全局异常处理了!

二、RestControllerAdvice注解使用

注解: @RestControllerAdvice@ControllerAdvice 是用来修饰类的,表示为一个增强类…我们定义全局异常拦截通常是使用 @RestControllerAdvice结合 @ExceptionHandler 来捕获绝大部分异常,然后统一返回Json形式…

例如:在我们的项目中,定义了这样一个全局数据返回对象…

后端返回数据都会构建为这样一个AjaxResult 对象返回给前端…

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AjaxResult {
    private Integer code;
    private String message;
    private Object data;


    public static AjaxResult success(Object data) {
       return AjaxResult.builder().code(200).message("success").data(data).build();
    }
    public static AjaxResult error(String message,Integer code) {
       return AjaxResult.builder().code(code).message(message).build();
    }
    public static AjaxResult error(String message) {
       return AjaxResult.builder().code(-1).message(message).build();
    }

}

那么,我们可以将拦截到的异常也构建为这样一个对象,返回给前端,前端根据code 判断请求是否成功,然后进行友好的提示即可…

(1)全局异常设置示例:

当我们假设有如下接口时: 很明显,当传参为0 时肯定会报错 除数不能为0…

image-20201021224916419

image-20201021224903973

我们针对该异常的捕获措施:

定义类,打上@RestControllerAdvice 注解,

public class ExceptionsHandler {}

编写捕获异常方法…

  /** 除数不能为0 */
  @ExceptionHandler({ArithmeticException.class})
  public AjaxResult arithmeticException(ArithmeticException ex) {
    log.error("除数不能为0:{} ", ex.getMessage(), ex);
    return AjaxResult.error("除数不能为0");
  }

如此设置后呢,服务器就能捕获到除数不能为0异常了!

image-20201021225159868

(2)自定义异常如何捕获

开发中,我们通常会自定义一些异常,并且抛出该异常,以达到程序终止或数据库操作回滚的目的…那么针对自定义的异常,我们又如何回滚呢??

首先,我们来自定一个异常…

@Data
public class CommonException extends RuntimeException {
    private String message;
    private Integer code=-1;

    public CommonException() {
    }
    public CommonException(String message) {
        this.message = message;
    }
}

编写业务代码,抛出我们的遗产信息

image-20201021232621565

然后呢,我们需要在增强类中,去捕获我们新增的异常…

  @ExceptionHandler(CommonException.class)
  public AjaxResult CommonException(CommonException exception) {
    log.error("公共异常抛出:{}", exception.getMessage());
    return AjaxResult.error("公共异常抛出",exception.getCode());
  }

这样就可以捕获到咱们自定的异常并返回给全端相同格式数据了!

image-20201021232732329

(3)常用异常捕获分享

下边是关于项目中,常用的一些异常捕获…

import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
 * @author leilei
 * @version 1.0
 * @date 2020/10/21 10:21
 * @desc: 全局异常拦截 返回json数据响应
 */
@Slf4j
@RestControllerAdvice
public class ExceptionsHandler {

  /** 运行时异常 */
  @ExceptionHandler(RuntimeException.class)
  public AjaxResult runtimeExceptionHandler(RuntimeException ex) {
    log.error("运行时异常:{}", ex.getMessage(), ex);
    return AjaxResult.error("运行时异常");
  }

  /** 空指针异常 */
  @ExceptionHandler(NullPointerException.class)
  public AjaxResult nullPointerExceptionHandler(NullPointerException ex) {
    log.error("空指针异常:{} ", ex.getMessage(), ex);
    return AjaxResult.error("空指针异常");
  }

  /** 类型转换异常 */
  @ExceptionHandler(ClassCastException.class)
  public AjaxResult classCastExceptionHandler(ClassCastException ex) {
    log.error("类型转换异常:{} ", ex.getMessage(), ex);
    return AjaxResult.error("类型转换异常");
  }
  /** 文件未找到异常 */
  @ExceptionHandler(FileNotFoundException.class)
  public AjaxResult FileNotFoundException(FileNotFoundException ex) {
    log.error("文件未找到异常:{} ", ex.getMessage(), ex);
    return AjaxResult.error("文件未找到异常");
  }
  /** 数字格式异常 */
  @ExceptionHandler(NumberFormatException.class)
  public AjaxResult NumberFormatException(NumberFormatException ex) {
    log.error("数字格式异常:{} ", ex.getMessage(), ex);
    return AjaxResult.error("数字格式异常");
  }
  /** 安全异常 */
  @ExceptionHandler(SecurityException.class)
  public AjaxResult SecurityException(SecurityException ex) {
    log.error("安全异常:{} ", ex.getMessage(), ex);
    return AjaxResult.error("安全异常");
  }
  /** sql异常 */
  @ExceptionHandler(SQLException.class)
  public AjaxResult SQLException(SQLException ex) {
    log.error("sql异常:{} ", ex.getMessage(), ex);
    return AjaxResult.error("sql异常");
  }
  /** 类型不存在异常 */
  @ExceptionHandler(TypeNotPresentException.class)
  public AjaxResult TypeNotPresentException(TypeNotPresentException ex) {
    log.error("类型不存在异常:{} ", ex.getMessage(), ex);
    return AjaxResult.error("类型不存在异常");
  }

  /** IO异常 */
  @ExceptionHandler(IOException.class)
  public AjaxResult iOExceptionHandler(IOException ex) {
    log.error("IO异常:{} ", ex.getMessage(), ex);
    return AjaxResult.error("IO异常");
  }

  /** 未知方法异常 */
  @ExceptionHandler(NoSuchMethodException.class)
  public AjaxResult noSuchMethodExceptionHandler(NoSuchMethodException ex) {
    log.error("未知方法异常:{} ", ex.getMessage(), ex);
    return AjaxResult.error("未知方法异常");
  }

  /** 数组越界异常 */
  @ExceptionHandler(IndexOutOfBoundsException.class)
  public AjaxResult indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException ex) {
    log.error("数组越界异常:{} ", ex.getMessage(), ex);
    return AjaxResult.error("数组越界异常");
  }
  /** sql语法错误异常 */
  @ExceptionHandler(BadSqlGrammarException.class)
  public AjaxResult BadSqlGrammarException(BadSqlGrammarException ex) {
    log.error("sql语法错误异常:{} ", ex.getMessage(), ex);
    return AjaxResult.error("sql语法错误异常");
  }

  /** 无法注入bean异常 */
  @ExceptionHandler(NoSuchBeanDefinitionException.class)
  public AjaxResult NoSuchBeanDefinitionException(NoSuchBeanDefinitionException ex) {
    log.error("无法注入bean异常 :{} ", ex.getMessage(), ex);
    return AjaxResult.error("无法注入bean");
  }

  /** Http消息不可读异常 */
  @ExceptionHandler({HttpMessageNotReadableException.class})
  public AjaxResult requestNotReadable(HttpMessageNotReadableException ex) {
    log.error("400错误..requestNotReadable:{} ", ex.getMessage(), ex);
    return AjaxResult.error("Http消息不可读");
  }

  /** 400错误 */
  @ExceptionHandler({TypeMismatchException.class})
  public AjaxResult requestTypeMismatch(TypeMismatchException ex) {
    log.error("400错误..TypeMismatchException:{} ", ex.getMessage(), ex);
    return AjaxResult.error("服务器异常");
  }

  /** 500错误 */
  @ExceptionHandler({ConversionNotSupportedException.class, HttpMessageNotWritableException.class})
  public AjaxResult server500(RuntimeException ex) {
    log.error("500错误:{} ", ex.getMessage(), ex);
    return AjaxResult.error("服务器异常");
  }

  /** 栈溢出 */
  @ExceptionHandler({StackOverflowError.class})
  public AjaxResult requestStackOverflow(StackOverflowError ex) {
    log.error("栈溢出:{} ", ex.getMessage(), ex);
    return AjaxResult.error("栈溢出异常");
  }

  /** 除数不能为0 */
  @ExceptionHandler({ArithmeticException.class})
  public AjaxResult arithmeticException(ArithmeticException ex) {
    log.error("除数不能为0:{} ", ex.getMessage(), ex);
    return AjaxResult.error("除数不能为0异常");
  }

  /** 其他错误 */
  @ExceptionHandler({Exception.class})
  public AjaxResult exception(Exception ex) {
    log.error("其他错误:{} ", ex.getMessage(), ex);
    return AjaxResult.error("网络连接失败,请退出后再试");
  }
}

处理后返给前端的异常如下

{
    "code": -1,
    "message": "索引越界异常",
    "data":null
}


{
    "code": -1,
    "message": "请求文件不存在异常",
    "data": null
}

需要注意的是.RestController ExceptionHandler 异常捕获并非万能,有些异常是捕获不了的…例如404等…

(4)404如何捕获?

我们都知道,web服务错误后,默认是将url映射到 /error

image-20201024180116462

其源码是这样处理的…

在收到错误信息后,返回了一个model andView …

image-20201024175137898

那么,我们可以,在服务器将错误请求映射url/error 时做一些处理,使其转发到我们定义的错误url

其实也很简单…定义一个类 实现springErrorController接口 即可

例如这样:

@RestController
public class MyExceptionController implements ErrorController {

    @Override
    public String getErrorPath() {
        return "/error";
    }

    @RequestMapping(value = {"/error"})
    public AjaxResult error(HttpServletRequest request, HttpServletResponse response) {
        int status = response.getStatus();
        if (status == 404) {
            return AjaxResult.error("404啦!!", 404);
        }
           return AjaxResult.error("错误了", -1);
    }
}

当服务器收到 /error 路径时转发到我们的/error,然后我们返回json数据即可…

实践操作:

当我们请求一个服务器不存在的url时候…

image-20201024180512584

如此,便是完成了对404异常的捕获了!

  • 17
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring Cloud Gateway是一个基于Spring实现的微服务网关,它提供了一种灵活、强大的方式来构建、管理和路由微服务的请求流量。在使用Spring Cloud Gateway时,我们可以通过编写自定义的全局过滤器来实现全局拦截异常的功能。 要实现全局拦截异常,首先我们需要创建一个全局异常处理的类,该类需要继承自AbstractGatewayExceptionHandler。在这个类中,我们可以根据自己的需求重写相应的方法来处理异常。 比如,可以重写handleException方法来处理异常,并返回自定义的错误响应。在这个方法中,我们可以根据不同的异常类型进行不同的处理,比如返回特定的错误码、错误信息等。 然后,我们需要将这个全局异常处理类注册到Spring Cloud Gateway中。在Java配置文件中,添加一个Bean,将全局异常处理类的实例作为参数传入。 通过以上步骤,我们就可以实现Spring Cloud Gateway的全局拦截异常功能了。当微服务中出现异常时,系统会自动调用全局异常处理类中的相应方法来处理异常,并返回自定义的错误响应。 总之,使用Spring Cloud Gateway可以方便地实现全局拦截异常的功能。我们只需要编写一个自定义的全局异常处理类,并将其注册到Spring Cloud Gateway中即可。这样,我们就能够在微服务中统一处理各种异常,并返回自定义的错误信息。这样不仅提高了系统的可维护性,还可以提升用户的体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值