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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识点-Spring Boot 统一异常处理汇总相关的知识,希望对你有一定的参考价值。

技术图片
长按二维码,关注我们

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

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

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

页面处理

首先我们来看页面错误的处理情况,当我们的程序内部报错的时候或者访问的页面找不到的时候,我们可以看到下面的错误页面:
技术图片
1.png
1.png
我们可以自己设计好看一点的页面来替换这个页面,这样会更友好点,比如我们看今日头条的页面:
技术图片
2.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的升级版》

更多技术分享请加我微信,我拉你进群进行交流:
技术图片

以上是关于知识点-Spring Boot 统一异常处理汇总的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 统一异常处理

基于Spring Boot的统一异常处理设计

基于spring boot的统一异常处理

Spring Boot? 统一异常处理

Spring Boot2 系列教程 (十四) | 统一异常处理

spring-boot实战07:Spring Boot中Web应用的统一异常处理