作者:BNDong

www.cnblogs.com/bndong/p/10135370.html

在启动应用时会发现在控制台打印的日志中出现了两个路径为 {[/error]} 的访问地址,当系统中发送异常错误时,Spring Boot 会根据请求方式分别跳转到以 JSON 格式或以界面显示的 /error 地址中显示错误信息。

2018-12-18 09:36:24.627  INFO 19040 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" ...
2018-12-18 09:36:24.632  INFO 19040 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" ...

默认异常处理

使用 AJAX 方式请求时返回的 JSON 格式错误信息。

{"timestamp": "2018-12-18T01:50:51.196+0000","status": 404,"error": "Not Found","message": "No handler found for GET /err404","path": "/err404"
}

使用浏览器请求时返回的错误信息界面。

自定义异常处理

引入依赖

<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.54</version>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

fastjson 是 JSON 序列化依赖, spring-boot-starter-freemarker 是一个模板引擎,用于我们设置错误输出模板。

增加配置

properties

# 出现错误时, 直接抛出异常(便于异常统一处理,否则捕获不到404)
spring.mvc.throw-exception-if-no-handler-found=true
# 不要为工程中的资源文件建立映射
spring.resources.add-mappings=false

yml

spring:# 出现错误时, 直接抛出异常(便于异常统一处理,否则捕获不到404)mvc:throw-exception-if-no-handler-found: true# 不要为工程中的资源文件建立映射resources:add-mappings: false

新建错误信息实体

/*** 信息实体*/
public class ExceptionEntity implements Serializable {private static final long serialVersionUID = 1L;private String message;private int    code;private String error;private String path;@JSONField(format = "yyyy-MM-dd hh:mm:ss")private Date timestamp = new Date();public static long getSerialVersionUID() {return serialVersionUID;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getError() {return error;}public void setError(String error) {this.error = error;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}public Date getTimestamp() {return timestamp;}public void setTimestamp(Date timestamp) {this.timestamp = timestamp;}
}

新建自定义异常

/*** 自定义异常*/
public class BasicException extends RuntimeException {private static final long serialVersionUID = 1L;private int code = 0;public BasicException(int code, String message) {super(message);this.code = code;}public int getCode() {return this.code;}
}
/*** 业务异常*/
public class BusinessException extends BasicException {private static final long serialVersionUID = 1L;public BusinessException(int code, String message) {super(code, message);}
}

BasicException 继承了 RuntimeException ,并在原有的 Message 基础上增加了错误码 code 的内容。而 BusinessException 则是在业务中具体使用的自定义异常类,起到了对不同的异常信息进行分类的作用。

新建 error.ftl 模板文件

位置:/src/main/resources/templates/ 用于显示错误信息

<!DOCTYPE html>
<html>
<head><meta name="robots" content="noindex,nofollow" /><meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"><style>h2{color: #4288ce;font-weight: 400;padding: 6px 0;margin: 6px 0 0;font-size: 18px;border-bottom: 1px solid #eee;}/* Exception Variables */.exception-var table{width: 100%;max-width: 500px;margin: 12px 0;box-sizing: border-box;table-layout:fixed;word-wrap:break-word;}.exception-var table caption{text-align: left;font-size: 16px;font-weight: bold;padding: 6px 0;}.exception-var table caption small{font-weight: 300;display: inline-block;margin-left: 10px;color: #ccc;}.exception-var table tbody{font-size: 13px;font-family: Consolas,"Liberation Mono",Courier,"微软雅黑";}.exception-var table td{padding: 0 6px;vertical-align: top;word-break: break-all;}.exception-var table td:first-child{width: 28%;font-weight: bold;white-space: nowrap;}.exception-var table td pre{margin: 0;}</style>
</head>
<body><div class="exception-var"><h2>Exception Datas</h2><table><tbody><tr><td>Code</td><td>${(exception.code)!}</td></tr><tr><td>Time</td><td>${(exception.timestamp?datetime)!}</td></tr><tr><td>Path</td><td>${(exception.path)!}</td></tr><tr><td>Exception</td><td>${(exception.error)!}</td></tr><tr><td>Message</td><td>${(exception.message)!}</td></tr></tbody></table>
</div>
</body>
</html>

编写全局异常控制类

/*** 全局异常控制类*/
@ControllerAdvice
public class GlobalExceptionHandler {/*** 404异常处理*/@ExceptionHandler(value = NoHandlerFoundException.class)@ResponseStatus(HttpStatus.NOT_FOUND)public ModelAndView errorHandler(HttpServletRequest request, NoHandlerFoundException exception, HttpServletResponse response) {return commonHandler(request, response,exception.getClass().getSimpleName(),HttpStatus.NOT_FOUND.value(),exception.getMessage());}/*** 405异常处理*/@ExceptionHandler(HttpRequestMethodNotSupportedException.class)public ModelAndView errorHandler(HttpServletRequest request, HttpRequestMethodNotSupportedException exception, HttpServletResponse response) {return commonHandler(request, response,exception.getClass().getSimpleName(),HttpStatus.METHOD_NOT_ALLOWED.value(),exception.getMessage());}/*** 415异常处理*/@ExceptionHandler(HttpMediaTypeNotSupportedException.class)public ModelAndView errorHandler(HttpServletRequest request, HttpMediaTypeNotSupportedException exception, HttpServletResponse response) {return commonHandler(request, response,exception.getClass().getSimpleName(),HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(),exception.getMessage());}/*** 500异常处理*/@ExceptionHandler(value = Exception.class)public ModelAndView errorHandler (HttpServletRequest request, Exception exception, HttpServletResponse response) {return commonHandler(request, response,exception.getClass().getSimpleName(),HttpStatus.INTERNAL_SERVER_ERROR.value(),exception.getMessage());}/*** 业务异常处理*/@ExceptionHandler(value = BasicException.class)private ModelAndView errorHandler (HttpServletRequest request, BasicException exception, HttpServletResponse response) {return commonHandler(request, response,exception.getClass().getSimpleName(),exception.getCode(),exception.getMessage());}/*** 表单验证异常处理*/@ExceptionHandler(value = BindException.class)@ResponseBodypublic ExceptionEntity validExceptionHandler(BindException exception, HttpServletRequest request, HttpServletResponse response) {List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();Map<String,String> errors = new HashMap<>();for (FieldError error:fieldErrors) {errors.put(error.getField(), error.getDefaultMessage());}ExceptionEntity entity = new ExceptionEntity();entity.setMessage(JSON.toJSONString(errors));entity.setPath(request.getRequestURI());entity.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());entity.setError(exception.getClass().getSimpleName());response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());return entity;}/*** 异常处理数据处理*/private ModelAndView commonHandler (HttpServletRequest request, HttpServletResponse response,String error, int httpCode, String message) {ExceptionEntity entity = new ExceptionEntity();entity.setPath(request.getRequestURI());entity.setError(error);entity.setCode(httpCode);entity.setMessage(message);return determineOutput(request, response, entity);}/*** 异常输出处理*/private ModelAndView determineOutput(HttpServletRequest request, HttpServletResponse response, ExceptionEntity entity) {if (!(request.getHeader("accept").contains("application/json")|| (request.getHeader("X-Requested-With") != null && request.getHeader("X-Requested-With").contains("XMLHttpRequest")))) {ModelAndView modelAndView = new ModelAndView("error");modelAndView.addObject("exception", entity);return modelAndView;} else {response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());response.setCharacterEncoding("UTF8");response.setHeader("Content-Type", "application/json");try {response.getWriter().write(ResultJsonTools.build(ResponseCodeConstant.SYSTEM_ERROR,ResponseMessageConstant.APP_EXCEPTION,JSONObject.parseObject(JSON.toJSONString(entity))));} catch (IOException e) {e.printStackTrace();}return null;}}
}

@ControllerAdvice

作用于类上,用于标识该类用于处理全局异常。

@ExceptionHandler

作用于方法上,用于对拦截的异常类型进行处理。value 属性用于指定具体的拦截异常类型,如果有多个 ExceptionHandler 存在,则需要指定不同的 value 类型,由于异常类拥有继承关系,所以 ExceptionHandler 会首先执行在继承树中靠前的异常类型。

BindException

该异常来自于表单验证框架 Hibernate validation,当字段验证未通过时会抛出此异常。

编写测试 Controller

@RestController
public class TestController {@RequestMapping(value = "err")public void error(){throw new BusinessException(400, "业务异常错误信息");}@RequestMapping(value = "err2")public void error2(){throw new NullPointerException("手动抛出异常信息");}@RequestMapping(value = "err3")public int error3(){int a = 10 / 0;return a;}
}

使用 AJAX 方式请求时返回的 JSON 格式错误信息。

# /err
{"msg": "应用程序异常","code": -1,"status_code": 0,"data": {"path": "/err","code": 400,"error": "BusinessException","message": "业务异常错误信息","timestamp": "2018-12-18 11:09:00"}
}# /err2
{"msg": "应用程序异常","code": -1,"status_code": 0,"data": {"path": "/err2","code": 500,"error": "NullPointerException","message": "手动抛出异常信息","timestamp": "2018-12-18 11:15:15"}
}# /err3
{"msg": "应用程序异常","code": -1,"status_code": 0,"data": {"path": "/err3","code": 500,"error": "ArithmeticException","message": "/ by zero","timestamp": "2018-12-18 11:15:46"}
}# /err404
{"msg": "应用程序异常","code": -1,"status_code": 0,"data": {"path": "/err404","code": 404,"error": "NoHandlerFoundException","message": "No handler found for GET /err404","timestamp": "2018-12-18 11:16:11"}
}

使用浏览器请求时返回的错误信息界面。

示例代码:https://github.com/BNDong/spring-cloud-examples/tree/master/spring-cloud-zuul/cloud-zuul

参考资料

《微服务 分布式架构开发实战》 龚鹏 著

https://www.jianshu.com/p/1a49fa436623

往期推荐

阿里云项目经理:Redis 开发规范

MySQL锁知识点复习,面试问到的概率超90%

【资源】这款工具让SpringBoot不再需要Controller、Service、DAO、Mapper!

ElasticSearch(ik分词器)+SpringBoot站内全文搜索解决方案

用王者荣耀来理解java设计模式之责任链模式

Spring Boot 万能文件在线预览解决方案

java多模块项目脚手架:Spring Boot + MyBatis 搭建教程

预防java项目的jar 被反编译的方法

SpringBoot 配置文件中的敏感信息如何保护?

案例:程序员离职在家,全职接单心得

图解 Spring 循环依赖,顶呱呱的好

(建议收藏)服务器宕机,效率排查攻略V2.0

回复干货】获取精选干货视频教程

回复加群】加入疑难问题攻坚交流群

回复mat】获取内存溢出问题分析详细文档教程

回复赚钱】获取用java写一个能赚钱的微信机器人

回复副业】获取程序员副业攻略一份

好文请点赞+分享

​Spring Cloud中统一异常处理是怎么做的?相关推荐

  1. Spring Cloud 如何统一异常处理?写得太好了!

    欢迎关注方志朋的博客,回复"666"获面试宝典 作者:BNDong 链接:www.cnblogs.com/bndong/p/10135370.html 前言 在启动应用时会发现在控 ...

  2. Spring Cloud Zuul统一异常处理

    一.在zuul的errorfilter中处理(我使用的这种) package com.mortals.iot.module.common.interceptor;import com.alibaba. ...

  3. ​Spring Cloud:统一异常处理

    作者:BNDong www.cnblogs.com/bndong/p/10135370.html 在启动应用时会发现在控制台打印的日志中出现了两个路径为 {[/error]} 的访问地址,当系统中发送 ...

  4. Spring Cloud:统一异常处理

    在启动应用时会发现在控制台打印的日志中出现了两个路径为 {[/error]} 的访问地址,当系统中发送异常错误时,Spring Boot 会根据请求方式分别跳转到以 JSON 格式或以界面显示的 /e ...

  5. Spring Cloud中Feign如何统一设置验证token

    前面我们大致的聊了下如何保证各个微服务之前调用的认证问题 Spring Cloud中如何保证各个微服务之间调用的安全性 Spring Cloud中如何保证各个微服务之间调用的安全性(下篇) 原理是通过 ...

  6. Spring Cloud中Hystrix仪表盘与Turbine集群监控

    Hystrix仪表盘,就像汽车的仪表盘实时显示汽车的各项数据一样,Hystrix仪表盘主要用来监控Hystrix的实时运行状态,通过它我们可以看到Hystrix的各项指标信息,从而快速发现系统中存在的 ...

  7. Spring Cloud中Hystrix仪表盘与Turbine集群监控 1

    Hystrix仪表盘,就像汽车的仪表盘实时显示汽车的各项数据一样,Hystrix仪表盘主要用来监控Hystrix的实时运行状态,通过它我们可以看到Hystrix的各项指标信息,从而快速发现系统中存在的 ...

  8. 【夯实Spring Cloud】Spring Cloud中使用Hystrix实现断路器原理详解(上)

    本文属于[夯实Spring Cloud]系列文章,该系列旨在用通俗易懂的语言,带大家了解和学习Spring Cloud技术,希望能给读者带来一些干货.系列目录如下: [夯实Spring Cloud]D ...

  9. Spring Cloud 中文文档

    Spring Cloud 官方文档 Spring Cloud为开发人员提供了用于快速构建分布式系统中某些常见模式的工具(例如,配置管理,服务发现,断路器,智能路由,微代理,控制总线).分布式系统的协调 ...

最新文章

  1. ZooKeeper 的典型应用场景
  2. 转一篇关于滑动窗口的讲解,挺详细的
  3. 小波的秘密6_小波包的理解
  4. php与java安全之争
  5. php转换图片属性a,PHP 提取图片img标记中的任意属性
  6. 链表_有序链表(给数组排序-应用)
  7. creator qt 字体太小_qt ttf 字体太小的解决方法
  8. 功能测试点有哪些?怎么做好软件功能测试?
  9. 一张图了解大牛直播SDK
  10. Cocos2d-x3.2 Menu菜单的创建
  11. excel自动排班表_18个施工进度计划横道图,Excel版自动生成表,操作简单明了
  12. Adobe Dreamweaver Adobe Photoshop CS5 amtlib.dll 文件
  13. java 自然常数e中出现的连续的第一个10个数字组成的质数_自然常数-常数e的来历e在很多数学公式中出现的频率比较高今天做导数题时看到 爱问知识人...
  14. 【阿卡乐谱】【日常分享】超级强大的简谱-《大海啊,故乡》
  15. kdj的matlab代码,8个字符的Kdj股票技术指标公式源代码(插图)
  16. opencv RGB 颜色 灰色图片显示
  17. python中最大值函数,python中如何获取最大值函数
  18. 如何实现网站内容秒收录?网站秒收录技巧分享!
  19. pod中mysql配置文件修改_Pod中的secret,configmap,downwardapi的使用记录
  20. 如果APP推广月预算只有10万 该如何展开推广?

热门文章

  1. python Gstreamer 播放不同编码格式的视频文件
  2. 公司/企业如何管理?管理技巧是什么?《宁向东管理学课》音频资料免费下载
  3. Java开发人员必备工具之 10 个大数据工具和框架
  4. Android11编译32go版本提示”32bit Kernels unsupported by MPGen“
  5. PYTHON h5py库包安装及读写
  6. win10“网络属性”选项卡空白怎么办
  7. 计算机网络显示空白,Win10以太网属性网络选项中显示为空白解决方法
  8. pybind11学习笔记
  9. 腾讯qlv格式转换mp4为何转换后只有音频
  10. 峰值电流检测电路设计/自己备忘