SpringBoot系列——自定义统一异常处理
前言
springboot内置的/error错误页面并不一定适用我们的项目,这时候就需要进行自定义统一异常处理,本文记录springboot进行自定义统一异常处理。
1、使用@ControllerAdvice、@RestControllerAdvice捕获运行时异常。
2、重写ErrorController,手动抛出自定义ErrorPageException异常,方便404、403等被统一处理。
代码
项目结构
引入我们父类pom即可,无需引入其他依赖
开始之前,需要先定下统一返回对象、自定义异常枚举类
/**
* 自定义异常枚举类
*/
public enum ErrorEnum {
//自定义系列
USER_NAME_IS_NOT_NULL("10001","【参数校验】用户名不能为空"),
PWD_IS_NOT_NULL("10002","【参数校验】密码不能为空"), //400系列
BAD_REQUEST("400","请求的数据格式不符!"),
UNAUTHORIZED("401","登录凭证过期!"),
FORBIDDEN("403","抱歉,你无权限访问!"),
NOT_FOUND("404", "请求的资源找不到!"), //500系列
INTERNAL_SERVER_ERROR("500", "服务器内部错误!"),
SERVICE_UNAVAILABLE("503","服务器正忙,请稍后再试!"), //未知异常
UNKNOWN("10000","未知异常!"); /** 错误码 */
private String code; /** 错误描述 */
private String msg; ErrorEnum(String code, String msg) {
this.code = code;
this.msg = msg;
} public String getCode() {
return code;
} public String getMsg() {
return msg;
}
}
/**
* 统一返回对象
*/ @Data
public class Result<T> implements Serializable {
/**
* 通信数据
*/
private T data;
/**
* 通信状态
*/
private boolean flag = true;
/**
* 通信描述
*/
private String msg = "操作成功"; /**
* 通过静态方法获取实例
*/
public static <T> Result<T> of(T data) {
return new Result<>(data);
} public static <T> Result<T> of(T data, boolean flag) {
return new Result<>(data, flag);
} public static <T> Result<T> of(T data, boolean flag, String msg) {
return new Result<>(data, flag, msg);
} public static <T> Result<T> error(ErrorEnum errorEnum) {
return new Result(errorEnum.getCode(), false, errorEnum.getMsg());
} @Deprecated
public Result() { } private Result(T data) {
this.data = data;
} private Result(T data, boolean flag) {
this.data = data;
this.flag = flag;
} private Result(T data, boolean flag, String msg) {
this.data = data;
this.flag = flag;
this.msg = msg;
} }
新增两个自定义异常,便于统一处理时捕获异常
/**
* 自定义业务异常
*/
public class ServiceException extends RuntimeException { /**
* 自定义异常枚举类
*/
private ErrorEnum errorEnum; /**
* 错误码
*/
private String code; /**
* 错误信息
*/
private String errorMsg; public ServiceException() {
super();
} public ServiceException(ErrorEnum errorEnum) {
super("{code:" + errorEnum.getCode() + ",errorMsg:" + errorEnum.getMsg() + "}");
this.errorEnum = errorEnum;
this.code = errorEnum.getCode();
this.errorMsg = errorEnum.getMsg();
} public ServiceException(String code,String errorMsg) {
super("{code:" + code + ",errorMsg:" + errorMsg + "}");
this.code = code;
this.errorMsg = errorMsg;
} public ErrorEnum getErrorEnum() {
return errorEnum;
} public String getErrorMsg() {
return errorMsg;
} public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
} public String getCode() {
return code;
} public void setCode(String code) {
this.code = code;
}
}
/**
* 自定义错误页面异常
*/
public class ErrorPageException extends ServiceException { public ErrorPageException(ErrorEnum errorEnum) {
super(errorEnum);
}
}
重写ErrorController,不在跳转原生错误页面,而是抛出我们的自定义异常
/**
* 自定义errorPage
*/
@Controller
public class ErrorPageConfig implements ErrorController{ private final static String ERROR_PATH = "/error" ; @Override
public String getErrorPath() {
return ERROR_PATH;
} @RequestMapping(ERROR_PATH)
public void errorPathHandler(HttpServletResponse response) {
//抛出ErrorPageException异常,方便被ExceptionHandlerConfig处理
ErrorEnum errorEnum;
switch (response.getStatus()) {
case 404:
errorEnum = ErrorEnum.NOT_FOUND;
break;
case 403:
errorEnum = ErrorEnum.FORBIDDEN;
break;
case 401:
errorEnum = ErrorEnum.UNAUTHORIZED;
break;
case 400:
errorEnum = ErrorEnum.BAD_REQUEST;
break;
default:
errorEnum = ErrorEnum.UNKNOWN;
break;
}
throw new ErrorPageException(errorEnum);
}
}
@RestControllerAdvice,统一异常处理,捕获并返回统一返回对象Result,同时把异常信息打印到日志中
/**
* 统一异常处理
*/
@Slf4j
@RestControllerAdvice
public class ExceptionHandlerConfig{ /**
* 业务异常 统一处理
*/
@ExceptionHandler(value = ServiceException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public Result exceptionHandler400(ServiceException e){
//把错误信息输入到日志中
log.error(ErrorUtil.errorInfoToString(e));
return Result.error(e.getErrorEnum());
} /**
* 错误页面异常 统一处理
*/
@ExceptionHandler(value = ErrorPageException.class)
@ResponseBody
public Result exceptionHandler(ErrorPageException e){
//把错误信息输入到日志中
log.error(ErrorUtil.errorInfoToString(e));
return Result.error(e.getErrorEnum());
} /**
* 空指针异常 统一处理
*/
@ExceptionHandler(value =NullPointerException.class)
@ResponseBody
public Result exceptionHandler500(NullPointerException e){
//把错误信息输入到日志中
log.error(ErrorUtil.errorInfoToString(e));
return Result.error(ErrorEnum.INTERNAL_SERVER_ERROR);
} /**
* 未知异常 统一处理
*/
@ExceptionHandler(value =Exception.class)
@ResponseBody
public Result exceptionHandler(Exception e){
//把错误信息输入到日志中
log.error(ErrorUtil.errorInfoToString(e));
return Result.error(ErrorEnum.UNKNOWN);
}
}
新建测试controller,新增几个测试接口,模拟多种异常报错的情况
/**
* 模拟异常测试
*/
@RestController
@RequestMapping("/test/")
public class TestController {
/**
* 正常返回数据
*/
@GetMapping("index")
public Result index(){
return Result.of("正常返回数据");
} /**
* 模拟空指针异常
*/
@GetMapping("nullPointerException")
public Result nullPointerException(){
//故意制造空指针异常
String msg = null;
msg.equals("huanzi-qch");
return Result.of("正常返回数据");
} /**
* 模拟业务异常,手动抛出业务异常
*/
@GetMapping("serviceException")
public Result serviceException(){
throw new ServiceException(ErrorEnum.USER_NAME_IS_NOT_NULL);
}
}
效果
正常数据返回
http://localhost:10010/test/index
模拟空指针异常
http://localhost:10010/test/nullPointerException
模拟业务异常
http://localhost:10010/test/serviceException
调用错误接口,404
http://localhost:10010/test/serviceException111
后记
自定义统一异常处理暂时先记录到这,后续再进行补充。
代码开源
代码已经开源、托管到我的GitHub、码云:
GitHub:https://github.com/huanzi-qch/springBoot
码云:https://gitee.com/huanzi-qch/springBoot
SpringBoot系列——自定义统一异常处理的更多相关文章
- Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群
Redis总结(五)缓存雪崩和缓存穿透等问题 前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...
- Spring MVC自定义统一异常处理类,并且在控制台中输出错误日志
在使用SimpleMappingExceptionResolver实现统一异常处理后(参考Spring MVC的异常统一处理方法), 发现出现异常时,log4j无法在控制台输出错误日志.因此需要自定义 ...
- springboot aop + logback + 统一异常处理 打印日志
1.src/resources路径下新建logback.xml 控制台彩色日志打印 info日志和异常日志分不同文件存储 每天自动生成日志 结合myibatis方便日志打印(debug模式) < ...
- Web API系列(三)统一异常处理
前面讲了webapi的安全验证和参数安全,不清楚的朋友,可以看看前面的文章,<Web API系列(二)接口安全和参数校验>,本文主要介绍Web API异常结果的处理.作为内部或者是对外提供 ...
- SpringBoot系列: Spring项目异常处理最佳实践
===================================自定义异常类===================================稍具规模的项目, 一般都要自定义一组异常类, 这 ...
- SpringBoot(七)_统一异常处理
我感觉看了这节课,给我的思考还是很多的,感觉受益良多.废话不多说,一起学习. 统一的 外层结构返回 这样利于代码看着也规范,前端处理也统一 # 错误返回 { "code": 1, ...
- SpringBoot统一异常处理后TX-LCN分布式事务无法捕获异常进行回滚
通常我们使用SpringBoot都会进行统一异常处理,例如写一个BaseController,在BaseController里进行统一异常处理,然后其他的Controller都继承BaseContro ...
- SpringCloud微服务实战——搭建企业级开发框架(七):自定义通用响应消息及统一异常处理
平时开发过程中,无可避免我们需要处理各类异常,所以这里我们在公共模块中自定义统一异常,Spring Boot 提供 @RestControllerAdvice 注解统一异常处理,我们在GitEgg ...
- HandlerExceptionResolver统一异常处理 返回JSON 和 ModelAndView
统一异常处理类的两种方式一种是前后分离,一种是一整套集合返回指定到指定的错误页面显示错误信息 1.由于前后分离,是统一返回JSON的格式 自定义Exception public class Bussi ...
随机推荐
- P1200_你的飞碟在这儿(JAVA语言)
题目描述 众所周知,在每一个彗星后都有一只UFO.这些UFO时常来收集地球上的忠诚支持者. 不幸的是,他们的飞碟每次出行都只能带上一组支持者.因此,他们要用一种聪明的方案让这些小组提前知道谁会被彗星带 ...
- Kubernetes声明式API与编程范式
声明式API vs 命令时API 计算机系统是分层的,也就是下层做一些支持的工作,暴露接口给上层用.注意:语言的本质是一种接口. 计算机的最下层是CPU指令,其本质就是用"变量定义+顺序执行 ...
- 灵雀云Istio技术实践专题整理
Istio技术实践专题(1) Service Mesh Istio 基本概念和架构基础 Istio被称作Kubernetes的最佳云原生拍档.从今天起,我们推出"Istio技术实践" ...
- 面试题-你听过TCP Fast Open (TFO/TCP快速打开)吗?能解释一下吗?
TCP Fast Open (TFO/TCP快速打开) TCP快速打开(TCP Fast Open,TFO)是什么? TCP快速打开(TCP Fast Open,TFO)是对TCP的一种简化握手手续的 ...
- Redis系列-存储篇sorted set主要操作命令
Redis系列-存储篇sorted set主要操作函数小结 redis支持有序集合,即sorted set.sorted set在set的基础上,增加了排序属性,是set的升级版.这里简要谈谈sort ...
- 让你的Windows/Linux玩上Switch!
1 前言 某天在Github上面看到了两个Switch的模拟器: yuzu Ryujinx 于是就想动手想尝试一下在Linux上面玩上Switch. 本文首先简单介绍一下两个模拟器,接着是两个模拟器的 ...
- Day03_16_递归
Java递归 递归包含两个部分 递归头: 标明了什么时候结束递归调用,如果没有递归头,程序将陷入死循环. 递归体: 标明了什么时候需要继续调用自身. 实例 import java.util.Scann ...
- (数据科学学习手札118)Python+Dash快速web应用开发——特殊部件篇
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...
- 通过钉钉网页上的js学习xss打cookie
做完了一个项目,然后没啥事做,无意看到了一个钉钉的外部链接: 题外话1: 查看源码,复制其中的代码: try { var search = location.search; if (search &a ...
- NumPy之:结构化数组详解
目录 简介 结构化数组中的字段field 结构化数据类型 创建结构化数据类型 从元组创建 从逗号分割的dtype创建 从字典创建 操作结构化数据类型 Offsets 和Alignment Field ...