首页 > 其他分享 >统一异常处理+错误编码设计方案

统一异常处理+错误编码设计方案

时间:2024-09-11 11:17:36浏览次数:8  
标签:编码 code return 错误 success 设计方案 Result public String

 

一、背景

1、系统在运行的时候可能会有下面这些种类的错误/失败发生:

(1) 依赖组件挂了,可能是 db,可能是 mq,可能是 cache。

(2)依赖服务挂了,可能是别人给你提供的 http/rpc 服务挂了。

(3)可能是你的依赖方超时了。

(4)可能是调用方的参数有问题。

(5)可能是调用方的参数无法正确地通过校验。

(6)可能是用户的某种操作在业务逻辑上不合理性,不能够接着让他执行下去

(7)还可能是程序自身出错了,比如数组越界,把 null 当成了某种合法的数据结构等等。

上面这些情况都是有很大概率发生的,当这种情况发生的时候,如果用户向你反馈了问题,你要怎么进行跟踪呢?

2、互联网公司在系统内部出了点什么问题的时候,展现给用户的是什么?白屏、无任何响应、nginx 504 Gateway Timeout。用户都不知道这些是什么。

二、目标

1、用户角度

对于产品的用户来讲,希望的是无论任何情况下都要有一个明确的反馈,正常情况下自不用说。而特殊情况下,也应该看得到系统到底出了什么问题,用户网络不行了就告诉用户是网络问题,不要出现一堆莫名其妙的英文。在各种异常情况下,要保证用户能够恢复到正常使用中去。不要显示用户看不懂的任何信息。不要什么都不显示(白屏)。用户的想法会要求在服务端就有完善的错误兜底。而不是写完正常的业务逻辑就完事了。

2、研发角度

实际上就是要有调用链的错误存储逻辑,比如errors 应该是能够一路把上游的错误串下来,而不是直接只存储当前这一级出了什么问题。

错误码最大的好处大概就是能够按照错误码建立自己的业务错误字典,这个字典你甚至可以在客户端进行存储,当用户使用报错的时候可以直接弹出错误原因自查选项以及恢复建议。错误码对于用户和客服,客服和技术人员之间沟通也有很大的好处,至少在软件使用和技术方面上的沟通成本会下降很多。

当看到错误码或者错误信息的时候,能马上找到代码的位置就事半功倍。

三、如何存储

1、所有错误消息都已经确定,可以写在程序里,用枚举比用全局常量更容易。

2、错误消息不能固定,可能在应用过程中添加,保存在数据库中。专门写一套管理类来处理,用 HashTable 或者 HashMap 之类的方式来实现。

3、把错误的key放在java的枚举里面,然后把错误的key和错误的信息的映射放在外部文件中,比如properties文件里。在运行时,根据错误枚举的key来实时从文件中取出错误文本就可以了,因为错误不是经常发生的,实时读取错误信息应该没有问题,当然也可以在程序启动时候把所有的错误信息读进来然后放到缓存里以提高性能。

四、设计错误编码的具体实现

1、统一设计目标

统一展示用户提示信息:编号“-网络异常,请联系相关人员处理!”

例如:x1010001-网络异常,请联系相关人员处理!

2、如何存储

在程序中使用枚举对错误编码进行存储。

3、如何获取

(1)系统bug级别的:直接在枚举中查看错误信息

(2)非系统bug级别的:设计单独的接口获取错误信息

综上所述:为了很好的扩展功能,建议设计单独的接口获取错误信息

4、具体编码设计

第1位(固定,用x标识,没有特殊设计含义,只是为了方便存储)

第2位(错误级别,1为非系统bug,2为系统bug需要改代码)

第3-4位(功能模块)

第5-8位(错误编码,从0001开始,依次顺延)

实例:x1010001

五、开发规范(在代码中使用“抛异常”还是“返回错误码”)

1、对于公司外的 http/api 开放接口必须使用“错误码”;而应用内部推荐异常抛出。

2、跨应用间 RPC 调用,优先考虑使用 Result 方式,封装 isSuccess()方法、“错误码”、“错误简短信息”。

  (1)关于 RPC 方法返回方式使用 Result 方式的理由

  使用抛异常返回方式,调用方如果没有捕获到就会产生运行时错误。

  如果不加栈信息,只是 new 自定义异常,加入自己的理解的 error message,对于调用端解决问题的帮助不会太多。

  如果加了栈信息,在频繁调用出错的情况下,数据序列化和传输的性能损耗也是问题。

  (2)具体实现

     统一返回结果展示,定义Result,封装 isSuccess()方法、“错误码”、“错误信息”。并提供ResultUtil工具类方便使用。

    public class Result<T> implements Serializable {
        private static final long serialVersionUID = 1L;
        private boolean success = false;//是否成功
        private String code;//状态码
        private String message;//信息
        private T data;//装载数据
        public Result() {}
        /**
         * @param success 是否成功
         */
        public Result(boolean success) {
             this.success = success;
        }
        /**
         * @param success 是否成功
         * @param message 消息
         */
        public Result(boolean success, String message) {
            this.success = success;
            this.message = message;
        }
        /**
         * @param success 是否成功
         * @param message 消息
         * @param data    数据
         */
        public Result(boolean success, String message, T data) {
            this.success = success;
            this.message = message;
            this.data = data;
        }
        /**
         * @param code  错误码
         * @param message 错误信息
         */
        public Result(String code, String message) {
            this.code = code;
            this.message = message;
        }
        public boolean isSuccess() {
            return success;
        }
       //省略get和set 
    }

    public class ResultUtil {
         /**
           * return success
           * @param data
           * @return
           */
          public static <T> Result<T> success(T data) {
              Result<T> result = new Result<T>();
              result.setCode("0");
              result.setData(data);
                        result.setSuccess(true);
              result.setMessage("success");
              return result;
          }
        /**
         * return success
         * @return
         */
        public static Result success() {
            return success(null);
        }
        /**
         * return error
         *
         * @param code 错误码
         * @param msg  错误信息
         * @return
         */
        public static Result error(String code, String msg) {
            Result result = new Result();
            result.setCode(code);
            result.setMessage(msg);
            return result;
        }
        /**
         * 枚举
         *
         * @param status
         * @return
         */
        public static Result error(ErrorCodeEnum status) {
            return error(status.getCode(), status.getMsg());
        }
    }

3、统一异常处理

(1)自定义异常类(运行时异常),并使用全局异常处理类GlobalErrorHandler统一处理所有异常。

若为json,直接返回Result,若为html,返回error页面,并显示错误信息。

(2)具体实现

public class CommonException extends RuntimeException{
    private static final long serialVersionUID = 1L;
    private String code;
    private String msg;
    
    public CommonException(String code,String msg){
        this.code = code;
        this.msg = msg;
    }
    public CommonException(ErrorCodeEnum resultEnum){
        this.code = resultEnum.getCode();
        this.msg = resultEnum.getMsg();
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
}

@ControllerAdvice
public class GlobalErrorHandler {
    private final static String DEFAULT_ERROR_VIEW = "error";//错误信息页
    
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Object handleException(Exception e,HttpServletRequest request) {
        Result obj = new Result();
        if (e instanceof CommonException) {
            CommonException commone = (CommonException)e;
            obj = ResultUtil.error(commone.getCode(), commone.getMsg());
        }else{
            obj = ResultUtil.error(ErrorCodeEnum.x9999999);
        }
        //使用HttpServletRequest中的header检测请求是否为ajax, 如果是ajax则返回json, 如果为非ajax则返回view(即ModelAndView)
          String contentTypeHeader = request.getHeader("Content-Type");
          String acceptHeader = request.getHeader("Accept");
          String xRequestedWith = request.getHeader("X-Requested-With");
          if ((contentTypeHeader != null && contentTypeHeader.contains("application/json"))
                  || (acceptHeader != null && acceptHeader.contains("application/json"))
                  || "XMLHttpRequest".equalsIgnoreCase(xRequestedWith)) {
              return obj;
          } else {
              ModelAndView modelAndView = new ModelAndView();
              modelAndView.addObject("code", obj.getCode());
              modelAndView.addObject("msg", obj.getMessage());
              modelAndView.addObject("url", request.getRequestURL());
              modelAndView.setViewName(DEFAULT_ERROR_VIEW);
              return modelAndView;
          }
    }
    
}

 

https://www.cnblogs.com/wd326150354/p/10861713.html

 

标签:编码,code,return,错误,success,设计方案,Result,public,String
From: https://www.cnblogs.com/erichi101/p/18407892

相关文章

  • uniapp 广告错误码
    一、app平台错误码codemessage-5001广告位标识adpid为空,请传入有效的adpid-5002无效的广告位标识adpid,请使用正确的adpid-5003未开通广告,请在广告平台申请并确保已审核通过-5004无广告模块,打包时请配置要使用的广告模块-5005广告加载失败,请过段时间重......
  • 解决python封装Logging模块后,log位置显示错误的问题
    引入今天由于项目需要,要将logging库二次封装成一个类,以实现一些自定义的功能。我将二次封装了一个logService类,然后在其中同样也实现info,warn,error等日志函数。额外加了一个将日志存入数据库的功能。大概是像下面这样子:但是在封装的过程中,出现了一个问题:log中,不能正确显......
  • MarsCode 用AI助力编码活动参与教程
    MarsCode用AI助力编码活动参与教程1.注册通过专有链接进入官网,并完成注册:https://www.marscode.cn/login?utm_source=community&utm_medium=jxh&utm_campaign=v0.1一定要在当前链接内完成注册也可通过手机扫码完成注册2.下载访问官网首页(marscode.cn),点击“立即获取编......
  • openwrt编译带源码信息的linux内核版本遇到的错误
    1、在openwrt的根目录中可以执行如下命令:makemenuconfig打开CONFIG_DEBUG选项就会生成待调试信息的内核版本2、在openwrt的根目录中可以执行如下命令:makekernel_menuconfig打开CONFIG_DEBUG_INFO也会生成待调试信息的内核版本问题描述:在gcc7.5.0和gdb8.1.1的环境中编译出来......
  • visual studio 2022 ,一打开项目就提示各种 内部错误,功能不可用
    原文链接:https://blog.csdn.net/weixin_45589116/article/details/133956055解决方法:  红色框中部分去掉 ......
  • 面试官:你是怎么处理vue项目中的错误的?
    一、错误类型任何一个框架,对于错误的处理都是一种必备的能力在Vue中,则是定义了一套对应的错误处理规则给到使用者,且在源代码级别,对部分必要的过程做了一定的错误处理。主要的错误来源包括:后端接口错误代码中本身逻辑错误二、如何处理后端接口错误通过axios的interc......
  • 多媒体应用设计师备考考点讲解(三):多媒体内容的编码与解码技术
    #多媒体应用设计师备考考点讲解(三):多媒体内容的编码与解码技术引言在现代多媒体应用中,编码与解码技术是至关重要的环节。无论是视频、音频还是图像,都需要通过编码技术将其转换为适合存储和传输的格式,而在接收端则需要通过解码技术将其还原为原始内容。本文将深入探讨多媒体内容的编......
  • 看demo学算法之 自编码器
    大家好,这里是小琳AI课堂!今天我们来聊聊自编码器。......
  • GEE错误:Image.select: Band pattern ‘BQA‘ did not match any bands. Available ban
    目录错误原始代码Landsat8TOA数据介绍错误解析正确的代码 结果错误Errorinmap(ID=LC08_044034_20130603):Image.select:Bandpattern'BQA'didnotmatchanybands.Availablebands:[B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,QA_PIXEL,QA_RADSAT......
  • 架构设计:负载均衡层设计方案(4)——LVS原理
    作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬学习必须往深处挖,挖的越深,基础越扎实!阶段1、深入多线程阶段2、深入多线程设计模式阶段3、深入juc源码解析阶段4、深入jdk其余源码解析......