作者: BNDong\
链接: https://www.cnblogs.com/bndon...

前言

在启动利用时会发现在控制台打印的日志中呈现了两个门路为 {[/error]} 的拜访地址,当零碎中发送异样谬误时,Spring Boot 会依据申请形式别离跳转到以 JSON 格局或以界面显示的 /error 地址中显示错误信息。

2018-12-18 09:36:24.627  INFO 19040 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" ...2018-12-18 09:36:24.632  INFO 19040 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" ...

Spring Boot 根底就不介绍了,举荐下这个实战教程:

https://github.com/javastacks...

默认异样解决

应用 AJAX 形式申请时返回的 JSON 格局错误信息。

{    "timestamp": "2018-12-18T01:50:51.196+0000",    "status": 404,    "error": "Not Found",    "message": "No handler found for GET /err404",    "path": "/err404"}

应用浏览器申请时返回的错误信息界面。

自定义异样解决

引入依赖

<dependency>    <groupId>com.alibaba</groupId>    <artifactId>fastjson</artifactId>    <version>1.2.54</version></dependency><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-freemarker</artifactId></dependency>

fastjson 是 JSON 序列化依赖, spring-boot-starter-freemarker 是一个模板引擎,用于咱们设置谬误输入模板。

减少配置

# 呈现谬误时, 间接抛出异样(便于异样对立解决,否则捕捉不到404)spring.mvc.throw-exception-if-no-handler-found=true# 不要为工程中的资源文件建设映射spring.resources.add-mappings=false
spring:  # 呈现谬误时, 间接抛出异样(便于异样对立解决,否则捕捉不到404)  mvc:    throw-exception-if-no-handler-found: true  # 不要为工程中的资源文件建设映射  resources:    add-mappings: false

Spring Boot 根底就不介绍了,举荐下这个实战教程:

https://github.com/javastacks...

新建错误信息实体

/** * 信息实体 */public class ExceptionEntity implements Serializable {    private static final long serialVersionUID = 1L;    private String message;    private int    code;    private String error;    private String path;    @JSONField(format = "yyyy-MM-dd hh:mm:ss")    private Date timestamp = new Date();    public static long getSerialVersionUID() {        return serialVersionUID;    }    public String getMessage() {        return message;    }    public void setMessage(String message) {        this.message = message;    }    public int getCode() {        return code;    }    public void setCode(int code) {        this.code = code;    }    public String getError() {        return error;    }    public void setError(String error) {        this.error = error;    }    public String getPath() {        return path;    }    public void setPath(String path) {        this.path = path;    }    public Date getTimestamp() {        return timestamp;    }    public void setTimestamp(Date timestamp) {        this.timestamp = timestamp;    }}

新建自定义异样

/** * 自定义异样 */public class BasicException extends RuntimeException {    private static final long serialVersionUID = 1L;    private int code = 0;    public BasicException(int code, String message) {        super(message);        this.code = code;    }    public int getCode() {        return this.code;    }}
/** * 业务异样 */public class BusinessException extends BasicException {    private static final long serialVersionUID = 1L;    public BusinessException(int code, String message) {        super(code, message);    }}

BasicException 继承了 RuntimeException ,并在原有的 Message 根底上减少了错误码 code 的内容。而 BusinessException 则是在业务中具体应用的自定义异样类,起到了对不同的异样信息进行分类的作用。

新建 error.ftl 模板文件

地位:/src/main/resources/templates/ 用于显示错误信息

<!DOCTYPE html><html><head>    <meta name="robots" content="noindex,nofollow" />    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">    <style>        h2{            color: #4288ce;            font-weight: 400;            padding: 6px 0;            margin: 6px 0 0;            font-size: 18px;            border-bottom: 1px solid #eee;        }        /* Exception Variables */        .exception-var table{            width: 100%;            max-width: 500px;            margin: 12px 0;            box-sizing: border-box;            table-layout:fixed;            word-wrap:break-word;        }        .exception-var table caption{            text-align: left;            font-size: 16px;            font-weight: bold;            padding: 6px 0;        }        .exception-var table caption small{            font-weight: 300;            display: inline-block;            margin-left: 10px;            color: #ccc;        }        .exception-var table tbody{            font-size: 13px;            font-family: Consolas,"Liberation Mono",Courier,"微软雅黑";        }        .exception-var table td{            padding: 0 6px;            vertical-align: top;            word-break: break-all;        }        .exception-var table td:first-child{            width: 28%;            font-weight: bold;            white-space: nowrap;        }        .exception-var table td pre{            margin: 0;        }    </style></head><body><div class="exception-var">    <h2>Exception Datas</h2>    <table>        <tbody>        <tr>            <td>Code</td>            <td>                ${(exception.code)!}            </td>        </tr>        <tr>            <td>Time</td>            <td>                ${(exception.timestamp?datetime)!}            </td>        </tr>        <tr>            <td>Path</td>            <td>                ${(exception.path)!}            </td>        </tr>        <tr>            <td>Exception</td>            <td>                ${(exception.error)!}            </td>        </tr>        <tr>            <td>Message</td>            <td>                ${(exception.message)!}            </td>        </tr>        </tbody>    </table></div></body></html>

编写全局异样管制类

/** * 全局异样管制类 */@ControllerAdvicepublic class GlobalExceptionHandler {    /**     * 404异样解决     */    @ExceptionHandler(value = NoHandlerFoundException.class)    @ResponseStatus(HttpStatus.NOT_FOUND)    public ModelAndView errorHandler(HttpServletRequest request, NoHandlerFoundException exception, HttpServletResponse response) {        return commonHandler(request, response,                exception.getClass().getSimpleName(),                HttpStatus.NOT_FOUND.value(),                exception.getMessage());    }    /**     * 405异样解决     */    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)    public ModelAndView errorHandler(HttpServletRequest request, HttpRequestMethodNotSupportedException exception, HttpServletResponse response) {        return commonHandler(request, response,                exception.getClass().getSimpleName(),                HttpStatus.METHOD_NOT_ALLOWED.value(),                exception.getMessage());    }    /**     * 415异样解决     */    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)    public ModelAndView errorHandler(HttpServletRequest request, HttpMediaTypeNotSupportedException exception, HttpServletResponse response) {        return commonHandler(request, response,                exception.getClass().getSimpleName(),                HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(),                exception.getMessage());    }    /**     * 500异样解决     */    @ExceptionHandler(value = Exception.class)    public ModelAndView errorHandler (HttpServletRequest request, Exception exception, HttpServletResponse response) {        return commonHandler(request, response,                exception.getClass().getSimpleName(),                HttpStatus.INTERNAL_SERVER_ERROR.value(),                exception.getMessage());    }    /**     * 业务异样解决     */    @ExceptionHandler(value = BasicException.class)    private ModelAndView errorHandler (HttpServletRequest request, BasicException exception, HttpServletResponse response) {        return commonHandler(request, response,                exception.getClass().getSimpleName(),                exception.getCode(),                exception.getMessage());    }    /**     * 表单验证异样解决     */    @ExceptionHandler(value = BindException.class)    @ResponseBody    public ExceptionEntity validExceptionHandler(BindException exception, HttpServletRequest request, HttpServletResponse response) {        List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();        Map<String,String> errors = new HashMap<>();        for (FieldError error:fieldErrors) {            errors.put(error.getField(), error.getDefaultMessage());        }        ExceptionEntity entity = new ExceptionEntity();        entity.setMessage(JSON.toJSONString(errors));        entity.setPath(request.getRequestURI());        entity.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());        entity.setError(exception.getClass().getSimpleName());        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());        return entity;    }    /**     * 异样解决数据处理     */    private ModelAndView commonHandler (HttpServletRequest request, HttpServletResponse response,                                            String error, int httpCode, String message) {        ExceptionEntity entity = new ExceptionEntity();        entity.setPath(request.getRequestURI());        entity.setError(error);        entity.setCode(httpCode);        entity.setMessage(message);        return determineOutput(request, response, entity);    }    /**     * 异样输入解决     */    private ModelAndView determineOutput(HttpServletRequest request, HttpServletResponse response, ExceptionEntity entity) {        if (!(                request.getHeader("accept").contains("application/json")                || (request.getHeader("X-Requested-With") != null && request.getHeader("X-Requested-With").contains("XMLHttpRequest"))        )) {            ModelAndView modelAndView = new ModelAndView("error");            modelAndView.addObject("exception", entity);            return modelAndView;        } else {            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());            response.setCharacterEncoding("UTF8");            response.setHeader("Content-Type", "application/json");            try {                response.getWriter().write(ResultJsonTools.build(                        ResponseCodeConstant.SYSTEM_ERROR,                        ResponseMessageConstant.APP_EXCEPTION,                        JSONObject.parseObject(JSON.toJSONString(entity))                ));            } catch (IOException e) {                e.printStackTrace();            }            return null;        }    }}

@ControllerAdvice

作用于类上,用于标识该类用于解决全局异样。

@ExceptionHandler

作用于办法上,用于对拦挡的异样类型进行解决。value 属性用于指定具体的拦挡异样类型,如果有多个 ExceptionHandler 存在,则须要指定不同的 value 类型,因为异样类领有继承关系,所以 ExceptionHandler 会首先执行在继承树中靠前的异样类型。

BindException

该异样来自于表单验证框架 Hibernate validation,当字段验证未通过时会抛出此异样。

编写测试 Controller

@RestControllerpublic class TestController {    @RequestMapping(value = "err")    public void error(){        throw new BusinessException(400, "业务异样错误信息");    }    @RequestMapping(value = "err2")    public void error2(){        throw new NullPointerException("手动抛出异样信息");    }    @RequestMapping(value = "err3")    public int error3(){        int a = 10 / 0;        return a;    }}

应用 AJAX 形式申请时返回的 JSON 格局错误信息。

# /err{    "msg": "应用程序异样",    "code": -1,    "status_code": 0,    "data": {        "path": "/err",        "code": 400,        "error": "BusinessException",        "message": "业务异样错误信息",        "timestamp": "2018-12-18 11:09:00"    }}# /err2{    "msg": "应用程序异样",    "code": -1,    "status_code": 0,    "data": {        "path": "/err2",        "code": 500,        "error": "NullPointerException",        "message": "手动抛出异样信息",        "timestamp": "2018-12-18 11:15:15"    }}# /err3{    "msg": "应用程序异样",    "code": -1,    "status_code": 0,    "data": {        "path": "/err3",        "code": 500,        "error": "ArithmeticException",        "message": "/ by zero",        "timestamp": "2018-12-18 11:15:46"    }}# /err404{    "msg": "应用程序异样",    "code": -1,    "status_code": 0,    "data": {        "path": "/err404",        "code": 404,        "error": "NoHandlerFoundException",        "message": "No handler found for GET /err404",        "timestamp": "2018-12-18 11:16:11"    }}

应用浏览器申请时返回的错误信息界面。

示例代码:https://github.com/BNDong/spr...

参考资料:

  • 《微服务 分布式架构开发实战》 龚鹏 著
  • https://www.jianshu.com/p/1a4...

近期热文举荐:

1.1,000+ 道 Java面试题及答案整顿(2022最新版)

2.劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4.别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!!

5.《Java开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞+转发哦!