知识点-Spring Boot 统一异常处理汇总

在实际开发中,我们会遇到很多异常,在发生异常的时候Spring Boot默认提供了错误页面展示给用户。看似比较友好,其实页面很丑。

上面讲的是做页面开发的时候遇到的问题,还有一种情况就是用来开发Rest接口,当错误的时候我们希望返回给用户的是我们接口的标准格式,不是返回一段html代码。

接下来分别给大家介绍下解决方案:

页面处理

首先我们来看页面错误的处理情况,当我们的程序内部报错的时候或者访问的页面找不到的时候,我们可以看到下面的错误页面:

1.png

我们可以自己设计好看一点的页面来替换这个页面,这样会更友好点,比如我们看今日头条的页面:

2.png

以前用Spring MVC时都是直接配置web.xml

<error-page>
    <error-code>404</error-code>
    <location>/WEB-INF/view/404.jsp</location>
</error-page>
<error-page>
    <error-code>400</error-code>
    <location>/WEB-INF/view/400.jsp</location>
</error-page>
<error-page>
    <error-code>500</error-code>
    <location>/WEB-INF/view/500.jsp</location>
</error-page>

在Spring Boot中也非常简单,直接编写对应的错误页面,进行覆盖即可:

/**
 * 自定义错误页面覆盖spring boot中的错误页面
 * @author yinjihuan
 *
 */
@Controller
public class ErrorController {
    @GetMapping("/400")
    public String badRequest() {
        return "error/400";
    }
    @GetMapping("/404")
    public String notFound() {
        return "error/404";
    }
    @GetMapping("/500")
    public String serverError() {
        return "error/500";
    }
}

页面内容可以自己写:

<body>
    <section id="error" class="container text-center" style="height:800px;">
        <h1>404, 页面没有找到</h1>
        <p>您正在寻找的页面不存在或发生其他错误。</p>
        <a class="btn btn-primary" href="http://pan.cxytiandi.com">回到网站首页</a> 
    </section>
</body>

REST接口处理

在开发rest接口时,我们往往会定义统一的返回格式,列如:

{
  "status": true,
  "code": 200,
  "message": null,
  "data": [
    {
      "id": "101",
      "name": "jack"
    },
    {
      "id": "102",
      "name": "jason"
    }
  ]
}

但是如果调用方请求我们的api时把接口地址写错了,就会得到一个404错误页面,最友好的方式就是返回固定的JSON格式,里面有个code为404。

所以我们需要在发生这种系统错误时也能返回我们自定义的那种格式

定义一个异常处理类

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
public class GlobalExceptionHandler {
    private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    /**
     * 系统异常处理,比如:404,500
     * @param req
     * @param resp
     * @param e
     * @return
     * @throws Exception
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseData defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        logger.error("", e);
        ResponseData r = new ResponseData();
        r.setMessage(e.getMessage());
        if (e instanceof org.springframework.web.servlet.NoHandlerFoundException) {
             r.setCode(404);
        } else {
             r.setCode(500);
        }
        r.setData(null);
        r.setStatus(false);
        return r;
    }
}

ResponseData是我们返回格式的实体类

public class ResponseData {
    private Boolean status = true;
    private int code = 200;
    private String message;
    private Object data;
}

这种在发生错误时这边会捕获到,然后封装好返回格式,返回给调用方

最后关键的一步是在spring boot的配置文件中加上如下配置:

#出现错误时, 直接抛出异常
spring.mvc.throw-exception-if-no-handler-found=true
#不要为我们工程中的资源文件建立映射
spring.resources.add-mappings=false

然后我们调用一个不存在的接口时,返回的错误信息就是我们自定义的那种格式了

{
  "status": false,
  "code": 404,
  "message": "No handler found for GET /rest11/auth",
  "data": null
}

页面错误和REST错误共存

当我们加好rest接口处理的方式后,访问页面不存在就会返回一段json数据,如果你的项目中既有rest接口,又有页面,这个时候就有冲突了。

我们可以通过为rest接口增加统一的访问前缀,比如:/rest/xxxx来区分请求,然后用@ControllerAdvice来分别处理:

import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
    private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    /**
     * 系统异常处理,比如:404,500
     * 
     * @param req
     * @param resp
     * @param e
     * @return
     * @throws Exception
     */
    @ExceptionHandler(value = Exception.class)
    // @ResponseBody
    public Object defaultErrorHandler(HttpServletRequest req, HttpServletResponse response, Exception e)
            throws Exception {
        logger.error("", e);
        if (req.getRequestURI().startsWith("/rest")) {
            ResponseData r = new ResponseData();
            r.setMessage(e.getMessage());
            if (e instanceof org.springframework.web.servlet.NoHandlerFoundException) {
                r.setCode(404);
            } else {
                r.setCode(500);
            }
            r.setData(null);
            r.setStatus(false);
            PrintWriter writer = response.getWriter();
            writer.println(JsonUtils.toJson(r));
            writer.flush();
            writer.close();
            return null;
        } else {
            if (e instanceof org.springframework.web.servlet.NoHandlerFoundException) {
                return "error/404";
            } else {
                return "error/500";
            }
        }
    }
}

推荐阅读:

  • 《Spring Boot 1.X和2.X优雅重启实战》
  • 《Spring Boot中快速操作Mongodb》
  • 《Spring JdbcTemplate的升级版》

原文发布于微信公众号 - 猿天地(cxytiandi)

原文发表时间:2018-05-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏玩转JavaEE

Spring Cloud中Hystrix的请求缓存

高并发环境下如果能处理好缓存就可以有效的减小服务器的压力,Java中有许多非常好用的缓存工具,比如Redis、EHCache等,当然在Spring Cloud的...

3688
来自专栏一个会写诗的程序员的博客

8.3 Spring Boot集成Scala混合Java开发参考资料

本章我们使用Spring Boot集成Scala混合Java开发一个Web性能测试平台。

1921
来自专栏小灰灰

Spring之RestTemplate中级使用篇

前面一篇介绍了如何使用RestTemplate发起post和get请求,然而也只能满足一些基本的场景,对于一些特殊的如需要设置请求头,添加认证信息等场景,却没有...

2701
来自专栏柠檬先生

SpringMVC——笔记

使用 @RequestMapping 映射请求 Spring MVC 使用@RequestMapping 注解为控制器指定可以处理那些URL请求。   在控制器...

2375
来自专栏IT笔记

SpringBoot开发案例之整合Dubbo提供者(一)

既然是开发案例,显然不会扯那么多老婆舌,有不清楚这两个东东的请自行百度。 ? 0.jpg 开发环境 JDK1.7、Maven、Eclipse、SpringBoo...

44513
来自专栏IT 指南者专栏

Spring框架系列(二)之Bean的注解管理

微信公众号:compassblog 欢迎关注、转发,互相学习,共同进步! 有任何问题,请后台留言联系! 1、Spring中的两种容器 在系列(一)中我们已经知道...

3436
来自专栏阿杜的世界

Java Web技术经验总结(八)

使用XML文件中的mvc:annoation-driven元素也可以,具体代码如下:

1073
来自专栏代码拾遗

深入理解Spring MVC

使用Spring Boot和web,thymeleaf的starter来设置初始工程。xml配置如下:

1132
来自专栏Hongten

HIbernate的“1+N”问题

import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.T...

1053
来自专栏WindCoder

Spring Boot REST API错误处理指南

本来是5号来的文章,无奈最近准备换工作,一直拖着没写,今天搜索偶然看见有人已经翻译完了,由于时间原因这次就直接转载下吧,现附上英文原文及相关信息,最后再附上译文...

2322

扫码关注云+社区

领取腾讯云代金券