在web项目中,不可避免地会出现系统异常,例如资源找不到的404错误,服务器异常的5**错误,如果对这些异常不进行任何处理,则是非常不友好的。今天就对springboot项目中的异常拦截处理进行一个简单的使用说明。这里我使用的是springboot默认的异常拦截,也就是新建一个BasicErrorController类继承BasicErrorController。
默认异常拦截
在springboot项目中,不管是404错误还是其他错误,浏览器访问的时候,都会抛出一个非常不友好的异常,如下
如果在客户端(非ajax)请求,则会返回一个json对象,如下
这样的异常是非常不友好的,接下来我们需要来对异常进行加工处理。
thymeleaf定制化页面
这里使用thymeleaf模板来新建页面。在pom中添加依赖即可。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.4.1</version>
</dependency>
新建一个继承BaseErrorController的类
在项目下新建一个error(可任意)包,新建一个继承BaseErrorController的类。如下
BaseErrorController里面有对异常进行拦截,我们可以进入到该类的源码中查看
可以看到在errorHtml()中返回了异常页面,在error()中返回了postman(非ajax)请求。
那么我们自定义的类继承该类写这两个方法,就可以实现异常的自定义拦截
package meicius.ori.error;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Controller
public class MyBasicErrorController extends BasicErrorController {
public MyBasicErrorController() {
super(new DefaultErrorAttributes(), new ErrorProperties());
}
@Override
protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
return super.getErrorAttributes(request, includeStackTrace);
}
@Override
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
ModelAndView modelAndView = super.errorHtml(request, response);
String httpStatus = modelAndView.getModel().get("status").toString();
if(httpStatus.equals("404")){
modelAndView.setViewName("/error/404");
return modelAndView;
}
Map<String, Object> stringObjectMap = getErrorAttributes(request, true);
modelAndView.addObject("stackTrace", stringObjectMap.get("trace").toString());
modelAndView.setViewName("/error/error");
return modelAndView;
}
@Override
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request,isIncludeStackTrace(request, MediaType.TEXT_HTML));
Map<String, Object> bodyRes = new HashMap<String, Object>();
bodyRes.put("message", "异常拦截");
bodyRes.put("data", body);
HttpStatus status = getStatus(request);
return new ResponseEntity<Map<String, Object>>(bodyRes, status);
}
}
代码中重写了errorHtml() 和 error()
errorHtml()方法
在该方法中,我做了一个404错误和非404错误的判断,如果是404错误,则会返回一个定制化的页面,只有一张图片。如果是非404错误,则会返回相关的错误信息。在templates中建立error文件夹,在该文件夹下面放抛出异常的页面
error.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org" layout:decorator="layout">
<head>
<title>Spring Boot ori统一异常拦截</title>
<script type="text/javascript">
</script>
<style>
*{
margin: 0;
padding: 0;
}
html, body, .container, .navbar{
height: 100%;
width: 100%;
overflow: hidden;
}
.navbar{
margin-left: 20px;
}
h1{
height: 60px;
}
h3{
height: 40px;
}
.debugDiv{
display: flex;
margin-left: 20px;
align-content: center;
margin-bottom: 20px;
}
.linkDiv{
width: 140px;
height: 60px;
margin-left: 10px;
line-height: 60px;
text-align: center;
}
.linkDiv:nth-child(1){
background-color: cornflowerblue;
}
.linkDiv:nth-child(2){
background-color: aquamarine;
}
.linkDiv:nth-child(3){
background-color: chocolate;
}
.trace{
width: 80%;
height: calc(100% - 400px);
overflow: auto !important;
border: 2px solid cornflowerblue;
padding: 5px;
}
.trace::-webkit-scrollbar{
width: 10px;
}
.trace::-webkit-scrollbar-track{
background-color: cornflowerblue;
/*-webkit-border-radius: 2em;*/
/*-moz-border-radius: 2em;*/
/*border-radius: 2em;*/
}
.trace::-webkit-scrollbar-thumb{
background-color: chocolate;
/*-webkit-border-radius: 2em;*/
/*-moz-border-radius: 2em;*/
/*border-radius: 2em;*/
}
</style>
</head>
<body>
<div class="container" layout:fragment="content" th:remove="tag">
<div class="navbar">
<h1 th:text="'ori 系统异常统一处理'"></h1>
<h3 th:text="'时间:'+${timestamp}"></h3>
<h3 th:text="'状态码:'+${status}"></h3>
<h3 th:text="'错误状态:'+${error}"></h3>
<h3 th:text="'地址:'+${path}"></h3>
<h3>调试</h3>
<div class="debugDiv">
<div class="linkDiv">
<a th:href="@{'https://www.google.com/webhp?hl=zh-CN#safe=strict&hl=zh-CN&q='+${error}}"
class="btn btn-default btn-lg" target="_blank" id="Google">Google</a>
</div>
<div class="linkDiv">
<a th:href="@{'https://www.baidu.com/s?wd='+${error}}"
class="btn btn-default btn-lg" target="_blank" id="Baidu">Baidu</a>
</div>
<div class="linkDiv">
<a th:href="@{'http://stackoverflow.com/search?q='+${error}}"
class="btn btn-default btn-lg" target="_blank" id="StackOverFlow">StackOverFlow</a>
</div>
</div>
<h3>异常堆栈跟踪日志StackTrace</h3>
<div class="trace" th:each="line:${stackTrace}">
<div th:text="${line}"></div>
</div>
</div>
</div>
</body>
</html>
404页面
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org" layout:decorator="layout">
<head>
<title>Spring Boot ori统一异常拦截</title>
<script type="text/javascript">
</script>
<style>
*{
margin: 0;
padding: 0;
}
html, body, .container{
height: 100%;
width: 100%;
overflow: hidden;
}
img{
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div class="container">
<img src="/404.png"/>
</div>
</body>
</html>
其中404.html页面引入了一张图片,图片放在static下面。整个目录如下
这样就完成了ajax请求的异常拦截
error()
error()方法里面是对非ajax请求的处理,例如使用postman请求
至此,springboot默认全局异常处理就完成了。这种方式不仅可以获取到异常的接口路径、错误状态、错误代码、还可以获取到堆栈日志等信息。