SpringBoot(六) -- SpringBoot错误处理机制
一.SpringBoot中的默认的错误处理机制
1.在SpringBootWeb开发中,当我们访问请求出现错误时,会返回一个默认的错误页面:
2.在使用其他客户端访问的时候,则返回一个json数据:
3.原理:可以参看原码ErrorMvcAutoConfiguration:
(1)给容器中添加了以下组件
DefaultErrorAttributes:帮我们在页面共享信息
@Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();
errorAttributes.put("timestamp", new Date());
addStatus(errorAttributes, requestAttributes);
addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
addPath(errorAttributes, requestAttributes);
return errorAttributes;
} private void addStatus(Map<String, Object> errorAttributes,
RequestAttributes requestAttributes) {
Integer status = getAttribute(requestAttributes,
"javax.servlet.error.status_code");
if (status == null) {
errorAttributes.put("status", 999);
errorAttributes.put("error", "None");
return;
}
errorAttributes.put("status", status);
try {
errorAttributes.put("error", HttpStatus.valueOf(status).getReasonPhrase());
}
catch (Exception ex) {
// Unable to obtain a reason
errorAttributes.put("error", "Http Status " + status);
}
}
--可获取到的属性
timestamp:时间戳
status:状态码
error:错误提示
exception:异常
message:异常消息
errors:JSR303数据校验的错误都在这
BasicErrorController:处理默认的/eerror请求
@RequestMapping(produces = "text/html")
public ModelAndView errorHtml(HttpServletRequest request,
HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
} @RequestMapping
@ResponseBody
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
return new ResponseEntity<Map<String, Object>>(body, status);
}
--将会存在两种情况,得到一个html页面或者一个JSON数据,在浏览器发起的请求中,可发现其请求头accept中标识着优先接收html页面:
--当其他客户端发送请求时,可以发现其请求头没有标注优先接收html数据(使用了*/*)
--浏览器发送的请求将来到错误的html页面,而其他客户端则会接收到一个错误的JSON数据.在浏览器发送错误请求时,使用了ModelAndView声明了当前错误页面的地址和页面内容.
protected ModelAndView resolveErrorView(HttpServletRequest request,
HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
for (ErrorViewResolver resolver : this.errorViewResolvers) {
ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
if (modelAndView != null) {
return modelAndView;
}
}
return null;
}
--在该方法中将所有的ErrorViewResolver都获取到,而后返回,而我们去哪个页面则是由ErrorViewResolver得到的.
ErrorPageCustomizer:系统出现错误时,来到error请求进行处理
DefaultErrorViewResolver:
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
Map<String, Object> model) {
ModelAndView modelAndView = resolve(String.valueOf(status), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
} private ModelAndView resolve(String viewName, Map<String, Object> model) {
String errorViewName = "error/" + viewName;
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
.getProvider(errorViewName, this.applicationContext);
if (provider != null) {
return new ModelAndView(errorViewName, model);
}
return resolveResource(errorViewName, model);
}
--SpringBoot可以去找到某个页面,error/404,如果模板引擎可以解析这个地址,那么就是用模板引擎解析.如果模板引擎不可用,就在静态资源文件夹下寻找errorViewName对应的页面
(2)一旦系统出现4XX或者5XX之类的错误,ErrorPageCustomizer就会生效(定制错误的响应规则),就会来到/error请求,就会被BasicErrorController进行处理
二.如何定制错误响应
1.如何定制错误的页面:在我们有模板引擎的情况下(error/404.html),将错误页面命名为错误状态码.html,放在模板引擎文件夹里面的error文件夹下,发生此状态码的错误就会来到对应的页面.如果我们为每一个状态码都配置一个错误页面的话,十分麻烦,我们可以给页面命名为4xx.html,这样当错误码不匹配的时候,将默认来到该html页面(优先寻找精确的状态码).
当没有相对应的模板引擎时,将会在静态资源文件夹中寻找
当模板引擎和静态资源的错误文件夹都没有的时候,将会来到SpringBoot的默认提示信息页面.
2.如何定制错误的JSON数据
package com.zhiyun.springboot.web_restfulcrud.exception; /**
* @author : S K Y
* @version :0.0.1
*/
public class UserNotExistException extends RuntimeException {
public UserNotExistException() {
super("用户不存在");
}
}
@ResponseBody
@RequestMapping("/hello")
public String hello(@RequestParam("user") String user) {
if (user.equals("AAA")) {
throw new UserNotExistException();
}
return "hello";
}
三.自定义JSON错误数据响应
1.使用SpringMvc的异常处理机制(ExceptionHandler)捕获到这个异常之后,运行自定义的方法
package com.zhiyun.springboot.web_restfulcrud.controller; import com.zhiyun.springboot.web_restfulcrud.entity.Employee;
import com.zhiyun.springboot.web_restfulcrud.exception.UserNotExistException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody; import java.util.HashMap;
import java.util.Map; /**
* @author : S K Y
* @version :0.0.1
*/
//在Spring中要成为一个异常处理器,需要使用该注解
@ControllerAdvice
public class MyExceptionHandler { @ResponseBody
@ExceptionHandler(UserNotExistException.class)
public Map<String, Object> handlerException(Exception e) {
Map<String, Object> map = new HashMap<>();
map.put("code", "user.not exist");
map.put("message", e.getMessage());
return map;
} }
2.到目前为止还没有实现我们的自适应效果(如果是浏览器访问则返回页面,其他客户端访问则是返回JSON数据):
@ExceptionHandler(UserNotExistException.class)
public String handlerException(Exception e) {
/* Map<String, Object> map = new HashMap<>();
map.put("code", "user.not exist");
map.put("message", e.getMessage());*/
//转发到error请求
return "forward:/error";
}
3.按照上述的方法,可以实现自适应的页面响应,但是此时响应的是默认的页面 ,想要实现自适应的效果,需要传入我们自定义的状态码:
@ExceptionHandler(UserNotExistException.class)
public String handlerException(Exception e, HttpServletRequest request) {
//传入我们自己的错误状态码
request.setAttribute("javax.servlet.error.status_code",500);
//转发到error请求
return "forward:/error";
}
4.将我们的定制数据携带出去:
(1)出现错误以后会来到error请求,这个请求会被BasicErrorController处理,他要返回的内容是由getErrorAttributes()方法得到的,而该方式是AbstractErrorController中随规定的方法,因此我们完全可以自定义一个Controller的实现类,或者是编写AbstractErrorController的子类来完成.
(2)页面上能用的数据或者是JSON返回能用的数据都是通过errorAttributes.getErrorAttributes(requestAttributes,includeStackTrace);来得到的
容器中DefaultErrorAttributes默认来进行数据处理,我们可以扩展该类来自定义返回信息:
package com.zhiyun.springboot.web_restfulcrud.component; import org.springframework.boot.autoconfigure.web.DefaultErrorAttributes;
import org.springframework.web.context.request.RequestAttributes; import java.util.Map; /**
* @author : S K Y
* @version :0.0.1
*/
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);
map.put("company", "skykuqi");
return map;
}
}
(3)想要传递更多的信息,我们可以使用request来进行传递
@ExceptionHandler(UserNotExistException.class)
public String handlerException(Exception e, HttpServletRequest request) {
//传入我们自己的错误状态码
request.setAttribute("javax.servlet.error.status_code", 500);
//传递错误信息
Map<String, Object> map = new HashMap<>();
map.put("code", "user.not exist");
map.put("message", e.getMessage());
request.setAttribute("errorMessage", map);
//转发到error请求
return "forward:/error";
}
package com.zhiyun.springboot.web_restfulcrud.component; import org.springframework.boot.autoconfigure.web.DefaultErrorAttributes;
import org.springframework.web.context.request.RequestAttributes; import java.util.Map; /**
* @author : S K Y
* @version :0.0.1
*/
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);
map.put("company", "skykuqi");
//自定义异常处理器所携带的数据
Map errorMap = (Map) requestAttributes.getAttribute("errorMessage", RequestAttributes.SCOPE_REQUEST);
map.put("errorMessage", errorMap);
return map;
}
}
--最终的效果:我们可以定制ErrorAttributes来进行自定义错误信息的响应
SpringBoot(六) -- SpringBoot错误处理机制的更多相关文章
- springboot(六)SpringBoot问题汇总
SpringBoot2.0整合Mybatis,取datetime数据类型字段出来时,发现少了8小时. 过程:mysql中注册时间查询出来结果是正确的,只是java程序运行出来后显示少了8小时.经前辈指 ...
- SpringBoot(六):SpringBoot中如何使用Servlet?
第一种方法: 1.使用Servlet3的注解方式编写一个Servlet 2.在main方法的主类上添加注解: @ServletComponentScan(basePackages = "co ...
- Springboot 错误处理机制
SpringBoot默认的错误处理机制 即我们常见的白色的ErrorPage页面 浏览器发送的请求头: 如果是其他的请求方式,比如客户端,则相应一个json数据: 原理:是通过 ErrorMvcAut ...
- 4_6.springboot2.xWeb开发之错误处理机制
1.SpringBoot默认的错误处理机制 默认效果:1).浏览器,返回一个默认的错误页面 浏览器发送请求的请求头: 2).如果是其他客户端,默认响应一个json数据 原理: 默认情况下,Sp ...
- Springboot 系列(七)Spring Boot web 开发之异常错误处理机制剖析
前言 相信大家在刚开始体验 Springboot 的时候一定会经常碰到这个页面,也就是访问一个不存在的页面的默认返回页面. 如果是其他客户端请求,如接口测试工具,会默认返回JSON数据. { &quo ...
- 【玩转SpringBoot】让错误处理重新由web服务器接管
其实web服务器是会处理错误的 在web.xml还是随处可见的年代时(确实有点老黄历了),下面的这些配置应该都不陌生. 根据错误代码处理错误,如下图01: 根据异常类型处理错误,如下图02: 不过我们 ...
- Netty(一) SpringBoot 整合长连接心跳机制
前言 Netty 是一个高性能的 NIO 网络框架,本文基于 SpringBoot 以常见的心跳机制来认识 Netty. 最终能达到的效果: 客户端每隔 N 秒检测是否需要发送心跳. 服务端也每隔 N ...
- 【玩转SpringBoot】SpringBoot应用的启动过程一览表
SpringBoot应用的启动方式很简单,就一行代码,如下图01: 其实这行代码背后主要执行两个方法,一个是构造方法,一个是run方法. 构造方法主要内容就是收集一些数据,和确认一些信息.如下图02: ...
- 【SpringBoot】SpringBoot Web开发(八)
本周介绍SpringBoot项目Web开发的项目内容,及常用的CRUD操作,阅读本章前请阅读[SpringBoot]SpringBoot与Thymeleaf模版(六)的相关内容 Web开发 项目搭建 ...
随机推荐
- JavaScript秒针转换00:00:00代码
var str = realFormatSecond(e.target.currentTime); console.log(e.target.scrollTop); //1255256252 c ...
- Zookeeper学习笔记(下)
这是ZK学习笔记的下篇, 主要希望可以分享一些 ZK 的应用以及其应用原理 我本人的学习告一段落, 不过还遗留了一些ZK相关的任务开发和性能测试的任务, 留待以后完成之后再通过其他文章来进行分享了 Z ...
- DevExpress v18.2版本亮点——Analytics Dashboard篇(一)
行业领先的.NET界面控件——DevExpress v18.2版本亮点详解,本文将介绍了DevExpress Analytics Dashboard v18.2 的版本亮点,新版30天免费试用!点击下 ...
- 【01】Python 环境变量、条件判断、循环、基本运算符
1 环境变量 1.1 Windows下环境变量 系统变量Path中要加入Python安装路径: C:\xxxx\Python36;C:\xxxx\Python36\Scripts; 2 条件判断 2. ...
- 理解Python中的__init__和__new__
先来看一段代码: class A(object): # -> don't forget the object specified as base def __new__(cls): print ...
- 前端面试题常考&必考之--跨域的解决办法
1.为啥出现跨域??? 在制定Html规则时,为了安全的考虑,一个源的脚本(网页,网站)不能与另一个源的资源进行交互, 所以就引发一个词叫做“同源策略”. 同源策略:同源策略是一种约定,它是浏览器最核 ...
- POJ 1161 Walls ( Floyd && 建图 )
题意 : 在某国,城市之间建起了长城,每一条长城连接两座城市.每条长城互不相交.因此,从一个区域到另一个区域,需要经过一些城镇或者穿过一些长城.任意两个城市A和B之间最多只有一条长城,一端在A城市, ...
- Vue中v-for配合使用Swiper插件问题
问题描述: 在一个页面中需要一个用swiper的轮播图,数据大概有40条,每一屏幕的swiper只显示其中的n条数据. 代码描述: <div id="app"> < ...
- 【PowerOJ1744&网络流24题】方格取数问题(最小割)
题意: n,m<=30 思路: [问题分析] 二分图点权最大独立集,转化为最小割模型,从而用最大流解决. [建模方法] 首先把棋盘黑白染色,使相邻格子颜色不同,所有黑色格子看做二分图X集合中顶点 ...
- 网页分页page
public class PageBean { private int page; // 第几页 private int pageSize; // 每页记录数 private int start; / ...