博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Cloud:统一异常处理
阅读量:6242 次
发布时间:2019-06-22

本文共 10279 字,大约阅读时间需要 34 分钟。

在启动应用时会发现在控制台打印的日志中出现了两个路径为 {[/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]}" ...

默认异常处理

使用 AJAX 方式请求时返回的 JSON 格式错误信息。

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

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

自定义异常处理

引入依赖

com.alibaba
fastjson
1.2.54
org.springframework.boot
spring-boot-starter-freemarker

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

增加配置

properties

# 出现错误时, 直接抛出异常(便于异常统一处理,否则捕获不到404)spring.mvc.throw-exception-if-no-handler-found=true# 不要为工程中的资源文件建立映射spring.resources.add-mappings=false

yml

spring:  # 出现错误时, 直接抛出异常(便于异常统一处理,否则捕获不到404)  mvc:    throw-exception-if-no-handler-found: true  # 不要为工程中的资源文件建立映射  resources:    add-mappings: false

新建错误信息实体

/** * 信息实体 */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/ 用于显示错误信息

    

Exception Datas

Code ${(exception.code)!}
Time ${(exception.timestamp?datetime)!}
Path ${(exception.path)!}
Exception ${(exception.error)!}
Message ${(exception.message)!}

编写全局异常控制类

/** * 全局异常控制类 */@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
fieldErrors = exception.getBindingResult().getFieldErrors(); Map
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 calidation,当字段验证未通过时会抛出此异常。

编写测试 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://www.jianshu.com/p/1a49fa436623

转载于:https://www.cnblogs.com/bndong/p/10135370.html

你可能感兴趣的文章
我的友情链接
查看>>
以太坊中的gas、gas price、gas limit到底是什么
查看>>
用户配置文件服务登录失败。无法加载用户配置文件
查看>>
com/android/dx/command/dexer/Main : Unsupported major.minor version 52.0
查看>>
我的友情链接
查看>>
四则运算法则表延伸 - 工厂方法模式
查看>>
我的友情链接
查看>>
话里话外:企业管理的五个层次
查看>>
Hazelcast集群服务(3)
查看>>
研究人员创建可***BIOS和网卡的恶意软件
查看>>
C++ numeric_limits的用法
查看>>
升级maildrop,解决自动回复乱码问题
查看>>
MySQL Sandbox---快速体验各版本MySQL
查看>>
我的友情链接
查看>>
CentOS安装KDE和Gnome
查看>>
非常有趣的js
查看>>
Spring 单元测试
查看>>
品读Mybatis源码---(1)解析配置文件
查看>>
android获取设备分辨率的新方法
查看>>
函数式对象之自指向
查看>>