SpringBoot——定制错误页面及原理

优质博文:IT-BLOG-CN

一、SpringBoot 默认的错误处理机制

【1】浏览器返回的默认错误页面如下:

浏览器发送请求的请求头信息如下: text/html会在后面的源码分析中说到。

【2】如果是其他客户端,默认则响应错误的 JSON字符串,如下所示:

其他客户端发送请求的请求头信息如下: */* 源码中解释。

二、原理分析

参照ErrorMvcAutoConfiguration类: 错误处理的自动配置类,以下4项为此类的重要信息。

【1】ErrorMvcAutoConfiguration.ErrorPageCustomizer: 当系统出现 4xx或者 5xx之类的错误时,ErrorPageCustomizer就会生效(定制错误的响应规则),根据如下源码可知,将会来到/error请求。

@Bean
public ErrorMvcAutoConfiguration.ErrorPageCustomizer errorPageCustomizer() {return new ErrorMvcAutoConfiguration.ErrorPageCustomizer(this.serverProperties);
}//进入ErrorPageCustomizer方法,发现registerErrorPages方法:注册一个错误也
private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {private final ServerProperties properties;protected ErrorPageCustomizer(ServerProperties properties) {this.properties = properties;}public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {ErrorPage errorPage = new ErrorPage(this.properties.getServletPrefix() + this.properties.getError().getPath());errorPageRegistry.addErrorPages(new ErrorPage[]{errorPage});}
}//进入this.properties.getError().getPath()方法,获取如下信息,得到/error请求。
@Value("${error.path:/error}")
private String path = "/error";//系统出现错误以后来到error请求进行处理;(web.xml注册的错误页面规则)

【2】BasicErrorController 处理 /error错误请求: 注意:text/html*/*就是在此处生效。

@Bean
@ConditionalOnMissingBean(value = {ErrorController.class},search = SearchStrategy.CURRENT
)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers);
}//进入BasicErrorController对象,获取如下信息
@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {private final ErrorProperties errorProperties;public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {this(errorAttributes, errorProperties, Collections.emptyList());}@RequestMapping(produces = {"text/html"}//产生html类型的数据;浏览器发送的请求来到这个方法处理)public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {HttpStatus status = this.getStatus(request);Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));response.setStatus(status.value());//去哪个页面作为错误页面;包含页面地址和页面内容ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);return modelAndView != null?modelAndView:new ModelAndView("error", model);}@RequestMapping@ResponseBody//产生json数据,其他客户端来到这个方法处理;public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));HttpStatus status = this.getStatus(request);return new ResponseEntity(body, status);}
}

如上代码中提到的错误页面解析代码,进入此方法: this.resolveErrorView(request, response, status, model);

protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) {Iterator var5 = this.errorViewResolvers.iterator();ModelAndView modelAndView;do {//从所有的ErrorViewResolver得到ModelAndViewif(!var5.hasNext()) {return null;}ErrorViewResolver resolver = (ErrorViewResolver)var5.next();modelAndView = resolver.resolveErrorView(request, status, model);} while(modelAndView == null);return modelAndView;
}

【3】最终的响应页面是由DefaultErrorViewResolver解析得到的: 最重要的信息是,SpringBoot默认模板引擎的/error目录下获取 ‘status’.xml 错误页面,也可以通过4xx.xml来统配 404.xml和 400.xml等等,但是优先获取精准的页面。如果模板引擎中不存在,则会从静态页面中获取错误页面。否则返回系统默认错误页面。

 @Bean@ConditionalOnBean({DispatcherServlet.class})@ConditionalOnMissingBeanpublic DefaultErrorViewResolver conventionErrorViewResolver() {return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);}//进入DefaultErrorViewResolver类中
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {ModelAndView modelAndView = this.resolve(String.valueOf(status), model);if(modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {//调用时viewname = status ***重要modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);}return modelAndView;
}private ModelAndView resolve(String viewName, Map<String, Object> model) {//默认SpringBoot可以去找到一个页面? error/404String errorViewName = "error/" + viewName;//模板引擎可以解析这个页面地址就用模板引擎解析TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);//模板引擎可用的情况下返回到errorViewName指定的视图地址,//当模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面 error/404.htmlreturn provider != null?new ModelAndView(errorViewName, model):this.resolveResource(errorViewName, model);
}

【4】DefaultErrorAttributes: 在页面添加错误信息,供我们使用。

@Bean
@ConditionalOnMissingBean(value = {ErrorAttributes.class},search = SearchStrategy.CURRENT
)
public DefaultErrorAttributes errorAttributes() {return new DefaultErrorAttributes();
}//进入DefaultErrorAttributes类中,发现此方法给视图中添加了status状态等信息,供我们使用。
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {Map<String, Object> errorAttributes = new LinkedHashMap();errorAttributes.put("timestamp", new Date());this.addStatus(errorAttributes, requestAttributes);this.addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);this.addPath(errorAttributes, requestAttributes);return errorAttributes;
}

三、定制错误 JSON数据

【1】自定义异常处理类,返回定制的 JSON数据。通过上述的分析,我们得知:
 ①、可以完全编写一个ErrorController的实现类,或者继承AbstractErrorController的子类,放入容器中。
 ②、也可以自定义异常处理类,返回 JSON数据。
 ③、页面上的数据或 JSON返回的数据都是可以通过errorAttributes.getErrorAttributes得到的。我们可以自定义属于自己的ErrorAttributes

//首先我们可以通过自定义异常处理,来确定返回的数据,但这个不够灵活,我们可以与③结合使用
/*** @RequestMapping启动应用后,被 @ExceptionHandler、@InitBinder、@ModelAttribute 注解的方法,都会作用在 被 @RequestMapping* 注解的方法上。*/
@ControllerAdvice
public class MyExceptionHandler {@ResponseBody@ExceptionHandler(UserNotExistException.class)public Map<String,Object> handlerException(Exception e, HttpServletRequest request){Map<String,Object> map = new HashMap<String,Object>();request.setAttribute("javax.servlet.error.status_code","500");map.put("code","user.notexist");map.put("message",e.getMessage());return map;}
}//③自定义ErrorAttributes,一定要加入容器
@Component
public class MyErrorAttributes extends DefaultErrorAttributes{@Overridepublic Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {//获取默认的配置,在此基础上添加自己的需求Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);//自定义自己需要的属性map.put("company","yintong");//获取我们在异常处理类中添加的信息,/*注意:当我们需要结合使用的时候异常处理必须return "forward:/error";将请求转发出去,不能直接返回map对象,同时要去掉@responseBody注解,否则ErrorAttributes不生效*/map.put("ext",requestAttributes.getAttribute("ext",requestAttributes.SCOPE_REQUEST));return map;}
}

【2】效果展示:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/216520.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

k8s集群资源监控工具metrics-server安装

1、下载镜像 docker pull swr.cn-east-2.myhuaweicloud.com/kuboard-dependency/metrics-server:v0.6.22、在任一一个主节点上创建角色&#xff0c;执行下面语句 kubectl create clusterrolebinding kube-proxy-cluster-admin --clusterrolecluster-admin --usersystem:kube-…

Spring框架学习 -- Bean的生命周期和作用域

目录 前言 案例 案例分析 作用域的定义 Bean对象的6种作用域 Singleton prototype 设置作用域 ​编辑延迟初始化 Spring的执行流程 Bean的生命周期 前言 我们可以类比一下普通变量的生命周期和作用域, 大多数变量的生命周期和作用域都被限定在了花括号内 {}, 除…

mysql索引分为哪几类,聚簇索引和非聚簇索引的区别,MySQL索引失效的情况有哪几种情况,MySQL索引优化的手段,MySQL回表

文章目录 索引分为哪几类&#xff1f;聚簇索引和非聚簇索引的区别什么是[聚簇索引](https://so.csdn.net/so/search?q聚簇索引&spm1001.2101.3001.7020)&#xff1f;&#xff08;重点&#xff09;非聚簇索引 聚簇索引和非聚簇索引的区别主要有以下几个&#xff1a;什么叫回…

python-选择排序

选择排序是一种简单直观的排序算法&#xff0c;它的基本思想是每一轮选择未排序部分的最小元素&#xff0c;然后将其放到已排序部分的末尾。这个过程持续进行&#xff0c;直到整个数组排序完成。(重点&#xff1a;通过位置找元素) 以下是选择排序的详细步骤和 Python 实现&…

原生小程序图表

原生小程序使用图表 话不多说直接进入正题 官方文档: https://www.ucharts.cn/v2/#/ 下载文件 首先去gitee上把文件下载到自己的项目中 https://gitee.com/uCharts/uCharts 找到微信小程序和里面的组件 把里面src下的文件全部下载下来放入自己项目中 项目文件 新建文件…

为何百兆静态库能打进数兆的可执行文件?

第三方库是工程开发必不可少的部分&#xff0c;而第三方库可以是.a和.framework的静态库&#xff0c;也可以是.framework的动态库&#xff0c;其中静态库是最常用的方式。 静态库往往比较大&#xff0c;可在打包到可执行文件之后&#xff0c;对安装包大小的增加远远小于静态库本…

党建信息管理系统源码 支持在线交党费 附带完整的搭建教程

传统的党建管理模式通常采用手工方式&#xff0c;不仅效率低下&#xff0c;而且容易出错。随着组织规模的扩大和党员数量的增加&#xff0c;这种管理方式已经无法满足现实需求。此外&#xff0c;传统的党建管理模式缺乏在线交党费功能&#xff0c;给党员带来不便。因此&#xf…

Linux 常见命令篇

history 获取执行的指令记录 语法格式: history [参数] 常用参数&#xff1a; -a 写入命令记录 -c 清空命令记录 -d 删除指定序号的命令记录 -n 读取命令记录 -r 读取命令记录到缓冲区 -s 将指定的命令添加到缓冲区 -w 将缓冲区信息写入到历史文件 history#获取最近的三条…

使用git下载远程所有分支到本地

使用git下载远程所有分支到本地&#xff1a; 打开gitbash 输入以下命令即可&#xff1a; git clone git地址 cd git文件夹 git branch -r | grep -v \-> | while read remote; do git branch --track "${remote#origin/}" "$remote"; done git fetch -…

如何用低代码的思路设计文字描边渐变组件

前言 文字特效设计一直是困扰 Web 前端 Css 世界多年的问题, 比如如何用纯 Css 实现文字描边, 渐变, 阴影等, 由于受限于浏览器兼容性的问题, 我们不得不使用其他替代方案来实现. 平时工作中我们使用 PS 等设计工具能很容易的实现文字渐变等特效, 但是随着可视化技术的成熟, 我…

IBM X3650M4安装ESXI6.5卡在/lsl_mr3.v00

环境&#xff1a;IBM X3650M4服务器双盘配置raid1&#xff0c;通过rufus制作启动U盘&#xff0c;安装VMware Vsphere 5.5系统 问题&#xff1a;卡在/lsi_mr3.v00界面无法往下运行&#xff08;两台配置一样的机器遇到同样的问题&#xff09; 解决方案&#xff1a; 直接在U盘根…

Spring 七大组件

文章目录 Spring 七大组件 Spring 七大组件 核心容器(Spring core) 核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean&#xff0c;它是工厂模式的实现。BeanFactory使用控制反转(IOC)模式…