点击上方 Java后端,选择 设为星标
介绍Spring Boot默认的异常处理机制
{
"timestamp": "2018-05-12T06:11:45.209+0000",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/index.html"
}
相关BasicErrorController中代码如下:
如何自定义错误页面
Whitelabel Error Page
的错误页面,这个很不友好,所以我们可以自定义下错误页面。
/resources/templates
下面创建error.html就可以覆盖默认的
Whitelabel Error Page
的错误页面,我项目用的是thymeleaf模板,对应的error.html代码如下:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
动态error错误页面
<p th:text="${error}"></p>
<p th:text="${status}"></p>
<p th:text="${message}"></p>
</body>
</html>
果只是静态HTML页面,不带错误信息的,在resources/public/下面创建error目录,在error目录下面创建对应的状态码html即可 ,例如,要将404映射到静态HTML文件,您的文件夹结构如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
静态404错误页面
</body>
</html>
静态404错误页面
错误页面
注:这时候如果存在上面第一种介绍的error.html页面,则状态码错误页面将覆盖error.html,具体状态码错误页面优先级比较高。
resources/templates/
下面创建error目录,在error目录下面命名即可:
@Controller
public class BaseErrorController extends AbstractController{
private Logger logger = LoggerFactory.getLogger(this.getClass());
@RequestMapping(value="/ex")
@ResponseBody
public String error(){
int i=5/0;
return "ex";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
动态500错误页面
<p th:text="${error}"></p>
<p th:text="${status}"></p>
<p th:text="${message}"></p>
</body>
</html>
注:如果同时存在静态页面500.html和动态模板的500.html,则后者覆盖前者。即
templates/error/
这个的优先级比
resources/public/error
高。
-
error.html会覆盖默认的 whitelabel Error Page 错误提示
-
静态错误页面优先级别比error.html高
-
动态模板错误页面优先级比静态错误页面高
@Configuration
public class ContainerConfig {
@Bean
public EmbeddedServletContainerCustomizer containerCustomizer(){
return new EmbeddedServletContainerCustomizer(){
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"));
}
};
}
}
HttpStatus.INTERNAL_SERVER_ERROR
就是对应500错误码,也就是说程序如果发生500错误,就会将请求转发到
/error/500
这个映射来,那我们只要实现一个方法是对应这个
/error/500
映射即可捕获这个异常做出处理
@RequestMapping("/error/500")
@ResponseBody
public String showServerError() {
return "server error";
}
/error/500
映射方法添加进来即可
@Controller
public class MyBasicErrorController extends BasicErrorController {
public MyBasicErrorController() {
super(new DefaultErrorAttributes(), new ErrorProperties());
}
/**
* 定义500的ModelAndView
* @param request
* @param response
* @return
*/
@RequestMapping(produces = "text/html",value = "/500")
public ModelAndView errorHtml500(HttpServletRequest request,HttpServletResponse response) {
response.setStatus(getStatus(request).value());
Map<String, Object> model = getErrorAttributes(request,isIncludeStackTrace(request, MediaType.TEXT_HTML));
model.put("msg","自定义错误信息");
return new ModelAndView("error/500", model);
}
/**
* 定义500的错误JSON信息
* @param request
* @return
*/
@RequestMapping(value = "/500")
@ResponseBody
public ResponseEntity<Map<String, Object>> error500(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request,isIncludeStackTrace(request, MediaType.TEXT_HTML));
HttpStatus status = getStatus(request);
return new ResponseEntity<Map<String, Object>>(body, status);
}
}
/error
,固我们方法里面对应的
@RequestMapping(produces = "text/html",value = "/500")
实际上完整的映射请求是
/error/500
,这就跟上面 customize 方法自定义的映射路径对上了。
<p th:text="${msg}"></p>
,如果输出结果有这个信息,则表示我们配置正确了。
通过@ControllerAdvice注解来处理异常
-
局部异常处理 @Controller + @ExceptionHandler
-
全局异常处理 @ControllerAdvice + @ExceptionHandler
局部异常处理 @Controller + @ExceptionHandler
Throwable、Exception 等异常对象;
ServletRequest、HttpServletRequest、ServletResponse、HttpServletResponse;
HttpSession 等会话对象;
org.springframework.web.context.request.WebRequest;
java.util.Locale;
java.io.InputStream、java.io.Reader;
java.io.OutputStream、java.io.Writer;
org.springframework.ui.Model;
ModelAndView;
org.springframework.ui.Model;
java.util.Map;
org.springframework.web.servlet.View;
@ResponseBody 注解标注的任意对象;
HttpEntity<?> or ResponseEntity<?>;
void;
@Controller
public class BaseErrorController extends AbstractController{
private Logger logger = LoggerFactory.getLogger(this.getClass());
@RequestMapping(value="/ex")
@ResponseBody
public String error(){
int i=5/0;
return "ex";
}
//局部异常处理
@ExceptionHandler(Exception.class)
@ResponseBody
public String exHandler(Exception e){
// 判断发生异常的类型是除0异常则做出响应
if(e instanceof ArithmeticException){
return "发生了除0异常";
}
// 未知的异常做出响应
return "发生了未知异常";
}
}
全局异常处理 @ControllerAdvice + @ExceptionHandler
/**
* 返回数据
*/
public class AjaxObject extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
public AjaxObject() {
put("code", 0);
}
public static AjaxObject error() {
return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员");
}
public static AjaxObject error(String msg) {
return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);
}
public static AjaxObject error(int code, String msg) {
AjaxObject r = new AjaxObject();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static AjaxObject ok(String msg) {
AjaxObject r = new AjaxObject();
r.put("msg", msg);
return r;
}
public static AjaxObject ok(Map<String, Object> map) {
AjaxObject r = new AjaxObject();
r.putAll(map);
return r;
}
public static AjaxObject ok() {
return new AjaxObject();
}
public AjaxObject put(String key, Object value) {
super.put(key, value);
return this;
}
public AjaxObject data(Object value) {
super.put("data", value);
return this;
}
public static AjaxObject apiError(String msg) {
return error(1, msg);
}
}
{
code:0,
msg:“获取列表成功”,
data:{
queryList :[]
}
}
{
code:500,
msg:“未知异常,请联系管理员”
}
public class BusinessException extends RuntimeException implements Serializable {
private static final long serialVersionUID = 1L;
private String msg;
private int code = 500;
public BusinessException(String msg) {
super(msg);
this.msg = msg;
}
public BusinessException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}
public BusinessException(int code,String msg) {
super(msg);
this.msg = msg;
this.code = code;
}
public BusinessException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
@Controller
public class BaseErrorController{
@RequestMapping("/json")
public void json(ModelMap modelMap) {
System.out.println(modelMap.get("author"));
int i=5/0;
}
}
/**
* 异常处理器
*/
@RestControllerAdvice
public class BusinessExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
* @param binder
*/
@InitBinder
public void initBinder(WebDataBinder binder) {
System.out.println("请求有参数才进来");
}
/**
* 把值绑定到Model中,使全局@RequestMapping可以获取到该值
* @param model
*/
@ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("author", "嘟嘟MD");
}
@ExceptionHandler(Exception.class)
public Object handleException(Exception e,HttpServletRequest req){
AjaxObject r = new AjaxObject();
//业务异常
if(e instanceof BusinessException){
r.put("code", ((BusinessException) e).getCode());
r.put("msg", ((BusinessException) e).getMsg());
}else{//系统异常
r.put("code","500");
r.put("msg","未知异常,请联系管理员");
}
//使用HttpServletRequest中的header检测请求是否为ajax, 如果是ajax则返回json, 如果为非ajax则返回view(即ModelAndView)
String contentTypeHeader = req.getHeader("Content-Type");
String acceptHeader = req.getHeader("Accept");
String xRequestedWith = req.getHeader("X-Requested-With");
if ((contentTypeHeader != null && contentTypeHeader.contains("application/json"))
|| (acceptHeader != null && acceptHeader.contains("application/json"))
|| "XMLHttpRequest".equalsIgnoreCase(xRequestedWith)) {
return r;
} else {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", e.getMessage());
modelAndView.addObject("url", req.getRequestURL());
modelAndView.addObject("stackTrace", e.getStackTrace());
modelAndView.setViewName("error");
return modelAndView;
}
}
}
再根据不同异常类型返回不同的相应,最后添加判断,如果是Ajax请求,则返回json,如果是非ajax则返回view,这里是返回到error.html页面。
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org" layout:decorator="layout">
<head>
<title>Spring Boot管理后台</title>
<script type="text/javascript">
</script>
</head>
<body>
<div layout:fragment="content" th:remove="tag">
<div id="navbar">
<h1>系统异常统一处理</h1>
<h3 th:text="'错误信息:'+${msg}"></h3>
<h3 th:text="'请求地址:'+${url}"></h3>
<h2>Debug</h2>
<a th:href="@{'https://www.google.com/webhp?hl=zh-CN#safe=strict&hl=zh-CN&q='+${msg}}"
class="btn btn-primary btn-lg" target="_blank" id="Google">Google</a>
<a th:href="@{'https://www.baidu.com/s?wd='+${msg}}" class="btn btn-info btn-lg" target="_blank" id="Baidu">Baidu</a>
<a th:href="@{'http://stackoverflow.com/search?q='+${msg}}"
class="btn btn-default btn-lg" target="_blank" id="StackOverFlow">StackOverFlow</a>
<h2>异常堆栈跟踪日志StackTrace</h2>
<div th:each="line:${stackTrace}">
<div th:text="${line}"></div>
</div>
</div>
</div>
<div layout:fragment="js" th:remove="tag">
</div>
</body>
</html>
{ "msg":"未知异常,请联系管理员", "code":500 }
荐
阅
读
为什么我不建议你去外包公司?
5. 团队开发中 Git 最佳实践
学Java,请关注公众号:Java后端
在看
本文分享自微信公众号 - Java后端(web_resource)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
相关文章
暂无评论...