Spring Cloud 如何统一异常处理?写得太好了!()

  本篇文章为你整理了Spring Cloud 如何统一异常处理?写得太好了!()的详细内容,包含有 Spring Cloud 如何统一异常处理?写得太好了!,希望能帮助你了解 Spring Cloud 如何统一异常处理?写得太好了!。

  分享Java技术,高并发编程,分布式技术,架构设计,Java面试题,算法,行业动态,程序人生等。

  
作者: BNDong
 

  链接: https:///bndong/p/10135370.html

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

  默认异常处理

  使用 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/spring-boot-best-practice

  新建错误信息实体

  

/**

 

   * 信息实体

  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

   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

   h2 Exception Datas /h2

   table

   tbody

   td Code /td

   ${(exception.code)!}

   /td

   /tr

   td Time /td

   ${(exception.timestamp?datetime)!}

   /td

   /tr

   td Path /td

   ${(exception.path)!}

   /td

   /tr

   td Exception /td

   ${(exception.error)!}

   /td

   /tr

   td Message /td

   ${(exception.message)!}

   /td

   /tr

   /tbody

   /table

   /div

   /body

   /html

  

 

  编写全局异常控制类

  

/**

 

   * 全局异常控制类

  @ControllerAdvice

  public 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

  

@RestController

 

  public 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/spring-cloud-examples/tree/master/spring-cloud-zuul/cloud-zuul

  参考资料:

  《微服务 分布式架构开发实战》 龚鹏 著

  https://www.jianshu.com/p/1a49fa436623

  近期热文推荐:

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

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

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

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

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

  觉得不错,别忘了随手点赞+转发哦!

  以上就是Spring Cloud 如何统一异常处理?写得太好了!()的详细内容,想要了解更多 Spring Cloud 如何统一异常处理?写得太好了!的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: