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””
或者是这样的页面
那么这个时候呢…由于杂乱不堪的错误提示页面,用户看不懂,所以对用户来说呢,体验很差
在开发前后端一体的单体项目时候呢,为了让异常在页面显示上更好看一点,让用户体验性更好一点,我们通常可以自定义一些精巧可爱的异常页 例如 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…
我们针对该异常的捕获措施:
定义类,打上@RestControllerAdvice
注解,
public class ExceptionsHandler {}
编写捕获异常方法…
/** 除数不能为0 */
@ExceptionHandler({ArithmeticException.class})
public AjaxResult arithmeticException(ArithmeticException ex) {
log.error("除数不能为0:{} ", ex.getMessage(), ex);
return AjaxResult.error("除数不能为0");
}
如此设置后呢,服务器就能捕获到除数不能为0异常了!
(2)自定义异常如何捕获
开发中,我们通常会自定义一些异常,并且抛出该异常,以达到程序终止或数据库操作回滚的目的…那么针对自定义的异常,我们又如何回滚呢??
首先,我们来自定一个异常…
@Data
public class CommonException extends RuntimeException {
private String message;
private Integer code=-1;
public CommonException() {
}
public CommonException(String message) {
this.message = message;
}
}
编写业务代码,抛出我们的遗产信息
然后呢,我们需要在增强类中,去捕获我们新增的异常…
@ExceptionHandler(CommonException.class)
public AjaxResult CommonException(CommonException exception) {
log.error("公共异常抛出:{}", exception.getMessage());
return AjaxResult.error("公共异常抛出",exception.getCode());
}
这样就可以捕获到咱们自定的异常并返回给全端相同格式数据了!
(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
其源码是这样处理的…
在收到错误信息后,返回了一个model andView …
那么,我们可以,在服务器将错误请求映射url/error
时做一些处理,使其转发到我们定义的错误url
其实也很简单…定义一个类 实现spring
的ErrorController
接口 即可
例如这样:
@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时候…
如此,便是完成了对404
异常的捕获了!