本篇要点

  • 介绍SpringBoot默认的异常处理机制。
  • 如何定义错误页面。
  • 如何自定义异常数据。
  • 如何自定义视图解析。
  • 介绍@ControllerAdvice注解处理异常。

一、SpringBoot默认的异常处理机制

默认情况下,SpringBoot为以下两种情况提供了不同的响应方式:

  1. Browser Clients浏览器客户端:通常情况下请求头中的Accept会包含text/html,如果未定义/error的请求处理,就会出现如下html页面:Whitelabel Error Page,关于error页面的定制,接下来会详细介绍。
  1. Machine Clients机器客户端:Ajax请求,返回ResponseEntity实体json字符串信息。
{"timestamp": "2020-10-30T15:01:17.353+00:00","status": 500,"error": "Internal Server Error","trace": "java.lang.ArithmeticException: / by zero...","message": "/ by zero","path": "/"
}

SpringBoot默认提供了程序出错的结果映射路径/error,这个请求的处理逻辑在BasicErrorController中处理,处理逻辑如下:

// 判断mediaType类型是否为text/html
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {HttpStatus status = getStatus(request);Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));response.setStatus(status.value());// 创建ModelAndView对象,返回页面ModelAndView modelAndView = resolveErrorView(request, response, status, model);return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {HttpStatus status = getStatus(request);if (status == HttpStatus.NO_CONTENT) {return new ResponseEntity<>(status);}Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));return new ResponseEntity<>(body, status);
}

二、错误页面的定制

相信Whitelabel Error Pag页面我们经常会遇到,这样体验不是很好,在SpringBoot中可以尝试定制错误页面,定制方式主要分静态和动态两种:

  1. 静态异常页面

在classpath:/public/error或classpath:/static/error路径下定义相关页面:文件名应为确切的状态代码,如404.html,或系列掩码,如4xx.html。

举个例子,如果你想匹配404或5开头的所有状态代码到静态的html文件,你的文件目录应该是下面这个样子:

src/+- main/+- java/|   + <source code>+- resources/+- public/+- error/|   +- 404.html|   +- 5xx.html+- <other public assets>

如果500.html和5xx.html同时生效,那么优先展示500.html页面。

  1. 使用模板的动态页面

放置在classpath:/templates/error路径下:这里使用thymeleaf模板举例:

src/+- main/+- java/|   + <source code>+- resources/+- templates/+- error/|   +- 5xx.html+- <other templates>

页面如下:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>5xx</title></head><body><h1>5xx</h1><p th:text="'error -->'+ ${error}"></p><p th:text="'status -->' + ${status}"></p><p th:text="'timestamp -->' + ${timestamp}"></p><p th:text="'message -->' + ${message}"></p><p th:text="'path -->' +${path}"></p><p th:text="'trace -->' + ${trace}"></p></body>
</html>

如果静态页面和动态页面同时存在且都能匹配,SpringBoot对于错误页面的优先展示规则如下: 如果发生了500错误: 动态500 -> 静态500 -> 动态5xx -> 静态5xx

三、自定义异常数据

默认的数据主要是以下几个,这些数据定义在org.springframework.boot.web.servlet.error.DefaultErrorAttributes中,数据的定义在getErrorAttributes方法中。

DefaultErrorAttributes类在org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration自动配置类中定义:

@Bean@ConditionalOnMissingBean(value = ErrorAttributes.class,search = SearchStrategy.CURRENT)public DefaultErrorAttributes errorAttributes() {return new DefaultErrorAttributes();}

如果我们没有提供ErrorAttributes的实例,SpringBoot默认提供一个DefaultErrorAttributes实例。

因此,我们就该知道如何去自定义异常数据属性:

  1. 实现ErrorAttributes接口。
  2. 继承DefaultErrorAttributes,本身已经定义对异常数据的处理,继承更具效率。

定义方式如下:

/*** 自定义异常数据* @author Summerday*/
@Slf4j
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {@Overridepublic Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {Map<String, Object> map = super.getErrorAttributes(webRequest, options);if(map.get("status").equals(500)){log.warn("服务器内部异常");}return map;}
}

四、自定义异常视图

自定义视图的加载逻辑存在于BasicErrorController类的errorHtml方法中,用于返回一个ModelAndView对象,这个方法中,首先通过getErrorAttributes获取到异常数据,然后调用resolveErrorView去创建一个ModelAndView对象,只有创建失败的时候,用户才会看到默认的错误提示页面。

@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {HttpStatus status = getStatus(request);Map<String, Object> model = Collections.unmodifiableMap(//ErrorAttributes # getErrorAttributesgetErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));response.setStatus(status.value());// EModelAndView modelAndView = resolveErrorView(request, response, status, model);return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}

DefaultErrorViewResolver类的resolveErrorView方法:

@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {// 以异常状态码作为视图名去locations路径中去查找页面ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);// 如果找不到,以4xx,5xx series去查找if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);}return modelAndView;
}

那么如何自定义呢?和自定义异常数据相同,如果我们定义了一个ErrorViewResolver的实例,默认的配置就会失效。

/*** 自定义异常视图解析* @author Summerday*/@Component
public class MyErrorViewResolver extends DefaultErrorViewResolver {public MyErrorViewResolver(ApplicationContext applicationContext, ResourceProperties resourceProperties) {super(applicationContext, resourceProperties);}@Overridepublic ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {return new ModelAndView("/hyh/resolve",model);}
}

此时,SpringBoot将会去/hyh目录下寻找resolve.html页面。

五、@ControllerAdvice注解处理异常

前后端分离的年代,后端往往需要向前端返回统一格式的json信息,以下为封装的AjaxResult对象:

public class AjaxResult extends HashMap<String, Object> {//状态码public static final String CODE_TAG = "code";//返回内容public static final String MSG_TAG = "msg";//数据对象public static final String DATA_TAG = "data";private static final long serialVersionUID = 1L;public AjaxResult() {}public AjaxResult(int code, String msg) {super.put(CODE_TAG, code);super.put(MSG_TAG, msg);}public AjaxResult(int code, String msg, Object data) {super.put(CODE_TAG, code);super.put(MSG_TAG, msg);if (data != null) {super.put(DATA_TAG, data);}}public static AjaxResult ok() {return AjaxResult.ok("操作成功");}public static AjaxResult ok(Object data) {return AjaxResult.ok("操作成功", data);}public static AjaxResult ok(String msg) {return AjaxResult.ok(msg, null);}public static AjaxResult ok(String msg, Object data) {return new AjaxResult(HttpStatus.OK.value(), msg, data);}public static AjaxResult error() {return AjaxResult.error("操作失败");}public static AjaxResult error(String msg) {return AjaxResult.error(msg, null);}public static AjaxResult error(String msg, Object data) {return new AjaxResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), msg, data);}public static AjaxResult error(int code, String msg) {return new AjaxResult(code, msg, null);}
}

根据业务的需求不同,我们往往也需要自定义异常类,便于维护:

/*** 自定义异常** @author Summerday*/
public class CustomException extends RuntimeException {private static final long serialVersionUID = 1L;private Integer code;private final String message;public CustomException(String message) {this.message = message;}public CustomException(String message, Integer code) {this.message = message;this.code = code;}public CustomException(String message, Throwable e) {super(message, e);this.message = message;}@Overridepublic String getMessage() {return message;}public Integer getCode() {return code;}
}

@ControllerAdvice和@RestControllerAdvice这俩注解的功能之一,就是做到Controller层面的异常处理,而两者的区别,与@Controller和@RestController差不多。

@ExceptionHandler指定需要处理的异常类,针对自定义异常,如果是ajax请求,返回json信息,如果是普通web请求,返回ModelAndView对象。

/*** 全局异常处理器* @author Summerday*/@RestControllerAdvice
public class GlobalExceptionHandler {private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);@ExceptionHandler(CustomException.class)public Object handle(HttpServletRequest request, CustomException e) {AjaxResult info = AjaxResult.error(e.getMessage());log.error(e.getMessage());// 判断是否为ajax请求if (isAjaxRequest(request)) {return info;}ModelAndView mv = new ModelAndView();mv.setViewName("custom"); // templates/custom.htmlmv.addAllObjects(info);mv.addObject("url", request.getRequestURL());return mv;}private boolean isAjaxRequest(HttpServletRequest request) {return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));}
}

在Controller层,人为定义抛出异常:

@RestController
public class TestController {@GetMapping("/ajax")public AjaxResult ajax() {double alpha = 0.9;if (Math.random() < alpha) {throw new CustomException("自定义异常!");}return AjaxResult.ok();}
}

最后,通过/templates/custom.html定义的动态模板页面展示数据:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>自定义界面</title></head><body><p th:text="'msg -->'+ ${msg}"></p><p th:text="'code -->'+ ${code}"></p><p th:text="'url -->'+ ${url}"></p></body>
</html>

源码下载

springboot获取静态图片路径_SpringBoot中的全局异常处理你确定你真的知道吗?相关推荐

  1. springboot获取静态图片路径_Springboot通过图片路径形式获取图片

    Springboot通过图片路径形式获取图片 一致以来都是用 http://127.0.0.1:8888/getPhoto?imgUrl=1.jpg 的形式获取数据,今天突然要 http://127. ...

  2. springboot获取静态图片路径_springboot为实体追加图片路径

    在项目中需要同时返回半路径和全路径两种 思考解决方案,想到的相对合理的方式 1.在application.properties配置图片资源库路径 例:host.url=http://www.baidu ...

  3. vue 直接访问静态图片_vue中本地静态图片路径写法

    这篇文章给大家介绍了vue中本地静态图片路径写法及Vue.js中引用图片路径的方式,需要的朋友参考下吧 这里写图片描述 需求:如何components里面的index.vue怎样能把assets里面的 ...

  4. Vue-在data中引入静态图片路径

    在data中引入静态图片路径 首先tempate中内容都一样: <template slot="pic" slot-scope="{ row }"> ...

  5. SpringBoot项目静态图片加载浏览器不显示问题解决方案

    SpringBoot项目静态图片加载浏览器不显示问题解决方案 项目结构如下: 我是通过Maven创建的以Thymeleaf为模板引擎创建的SpringBoot Web项目,发现加载的图片在浏览器不显示 ...

  6. Android 华为手机获取相册图片路径,获取不到问题

    未经本人授权,不得转载!否则必将维权到底 有个需求,可以从系统相册选择图片,上传到服务器.那么选择从系统相册选择完图片后,图片的名字需要显示在页面上.这里出了个 Bug,华为手机用系统的方法,获取相册 ...

  7. springboot+aop+自定义注解,打造通用的全局异常处理和参数校验切面(通用版)

    springboot+aop+自定义注解,打造通用的全局异常处理和参数校验切面(通用版) 参考文章: (1)springboot+aop+自定义注解,打造通用的全局异常处理和参数校验切面(通用版) ( ...

  8. vue 动态获取的图片路径不显示_Vue中img的src是动态渲染时不显示的解决

    Vue中img的src是动态渲染时不显示的解决 今天在项目中遇到一个需求,设计稿如下 就是展示用户头像,数据从后端获取,要是没有拿到则显示默认图片. 项目采用vue开发,本人也是第一次在实际项目中使用 ...

  9. 在java中图片路径_java中获取图片路径三中方法

    java中获取图片路径的方法 //获取路径三中方法 //+ f.getOriginalFilename(); //String filedir = Thread.currentThread().get ...

最新文章

  1. 全线衰退:PC产业一枝孤秀
  2. linux 测试各大网站速度curl
  3. 用EC5/EC6自定义class的区别及用法 -- Phaser3网页游戏框架
  4. so 问题来了,你现在值多少钱?
  5. Crawler:爬虫之基于https+parse库实现爬取国内某知名招聘网上海、北京关于区块链职位的求职信息
  6. SpringMVC核心——视图渲染(包含视图解析)问题
  7. 中科院三年连发三个诚信提醒文件:论文署名规范、科研原始记录、生医研究伦理...
  8. 云图说|DRS数据对比——带您随时观测数据一致性
  9. 实录:有钱男性的真实私生活
  10. 最简单的BufferQueue测试程序(九)
  11. linux mmu的实现的讲解_Linux_MMU
  12. 用C语言实现C++ 继承与多态
  13. Windows取证一
  14. ap计算机知识点总结,AP统计学考试知识点汇总
  15. S7-1200中时钟功能设定和读写调用的具体方法
  16. 计算机文化基础—IT概论
  17. steam服务器维护6月28,绝地求生6月28日更新到几点 吃鸡更新维护公告
  18. 狄克斯特拉算法(Dijkstra)详细解释
  19. FART脱壳机的使用与进阶(1)_FART的安装与使用(pixel为例)
  20. web页面之弹出窗口

热门文章

  1. Team Foundation(通常记作“TFS”)
  2. cmake中的变量和命令的大小写
  3. 关于mysql的wait_timeout参数 设置不生效的问题
  4. 位居新品第一、单品第二,乐视1s吊打了谁的耳光?
  5. 排错之网络映射缓存凭证记录导致备份计划任务失败
  6. XP支持4G以上物理内存的方法
  7. python程序把文件编码转换
  8. 小女子需要各位博友帮忙—— 一个关于JS 动态表格合并拆分问题
  9. SQL Server 行转列,列转行。多行转成一列
  10. springboot接口入参下划线转驼峰以及返回参数驼峰转下划线实现