写点什么

如何定义错误码

用户头像
编号94530
关注
发布于: 2021 年 03 月 24 日
如何定义错误码

好久没有写文章了,搞得不知道写啥,最近遇到了一个问题,决定把东西总结一下,记录下来。


1. 背景


最近开发,发现遇到了一个问题,那就是关于如何定义错误码。写代码写着写着迷糊了,突然发现不知道如何定义错误码,错误码对我们来说,到底有什么用?今天我结合自己的实现,来说一下如何定义错误码


2. 处理错误的方式


在 Spring 时代,spring 提供的 ControllerAdvice,RestControllerAdvice 给我们集中处理异常提供了一种很好的解决方案。我们也经常用这样的方式来解决方案。但是,结合我们一部分常用的也给我带来了一部分烦恼,我来说一下为什么。


2.1 处理方案


在我们自己的代码中,经常会定义一种通用的返回结果,大多数都叫 Result。代码如下:


@Data@AllArgsConstructorpublic class Result<T> implements Serializable {    /**     * 状态码     */    private Object code;
/*** * 说明信息 */ private String message;
/** * 是否成功 */ private boolean success;
/** * 返回数据 */ private T data;}
复制代码


我们经常会用这个对象当做接口的公共返回,所以理所当然的,在定义公共异常处理逻辑的时候,也会写成如下的样子。代码如下:


@RestControllerAdvice@Slf4jpublic class DefaultExceptionHandler {
/** * 业务异常统一捕获 * * @param req 请求 * @param e 业务异常 * @return 对应结果 */ @ExceptionHandler(value = BizException.class) public Result<String> businessExceptionHandler(HttpServletRequest req, BizException e) { // 已知异常不打印堆栈,避免多余的日志IO输出 return new Result<>(e.getCode(), e.getMessage()); }}
复制代码


这是一个处理公共异常的类,里面也理所当然的返回了 Result 对象,看起来似乎都是天衣无缝,没什么问题,但是,也给我带来了一些困惑。


2.2 我的困惑


当我们遇到异常的时候,会写下如下的代码:


throw new BizException(errorCode, errorMessage);


错误码是我们自己定义的,例如:errorCode=400, errorMsg="session timeout",然后这个异常会理所当然的被我们的公共异常处理器捕获,然后返回给前端一个公共的 Result,前端拿到状态码来处理相应的逻辑,例如:跳转到登录页面。这一切看起来都是正常的,但是,有个但是,如果我们返回的状态码不是 400 的时候呢? 或者有其他的状态码的时候呢?600, 601,602 ,前端都要一一处理吗?这个说实话,看起来怪怪的,处理的要求太多了,前端的改动也太大了。还有一个问题是每个请求都返回的 HttpStatus 都是 200,给测试也带来了一定的烦恼,测试需要认真仔细,一一的看你的接口返回的 内容,来判断你的返回内容是都正确, 这无疑是增加了许多的工作量。看起来状态码似乎也没带来一个好的效果。


2.3 我的思考


就我目前遇到的情况来说,我觉得错误码是一个可有可无的东西,因为我只需要判断 success 是否为 True 就好了,因为当 success 为 false 的时候,都需要抛出 message 来展示。那我是不是可以随意定义错误码了呢?如果想要用上错误码,怎么用最合适,最有价值呢?错误码用来做错误接口提示可以吗?


3. 解开迷惑


对错误码理解少,或者说当错误直接提示 message,或者说想在发生异常的时候,通过链路 id 或者其他方式找异常的我们都太天真了。我顿悟是在一张图上,图上的内容大概如下。

业务处理情况


看到这个图的第一反应,我觉得需要定义好多错误码,好麻烦,能不能给出一种公共的,然后让前端好处理一些。并且我想改变我的代码,不想让测试费劲的看哪个接口出问题,便于我自己找问题,也便于测试。而且我还想前端基于目前的代码,不要有太大的改动,就算有改动,没覆盖到的地方,在以后的迭代中进行修改。


思前想后,想到一个这样的处理逻辑,一下子处理的当前的困境。


3.1 如何定义错误码


想来想去,觉得这样的定义错误码蛮合适的,给人一种靠谱的感觉。


错误码公式:系统编码 + 业务编码 + 错误码 + 接口编码


3.1.1 系统编码


系统编码:系统编码就是对每个系统进行编码,如有三个系统 A,B,C,然后我进行了编号,分别为: 01,02,03,通过这几个数字,我就可以找到对应的系统。


3.1.2 业务编码


业务编码:业务编码就是对应具体的业务,如:我的系统中涉及到了 D,E,F 三个业务,然后我对三个业务也进行了编码,分别是 100,200,300,通过这几个数字,我也可以找对对应的业务。


3.1.3 错误码


错误码就是要根据具体的情况来了,如下。


public enum SystemStatusEnum implements BaseEnum<Integer> {    /**     * 接口熔断     */    HYSTRIX_RETURN_CODE(602, "接口熔断"),
/** * 业务异常 */ BUSINESS_ERROR(600, "业务异常"),}
复制代码


这些就要根据具体的情况,来定义一些编码,便于知道当出现这些异常的时候,需要如果做处理。


3.1.4 接口编码


接口编码,就是对每个接口进行编码,如:D 业务的接口有:m,n 两个接口。定义一个接口编码如下:


/** * <br>接口编码</br> * * @author fattyca1 * @since 1.0 */public class InterfaceCode {    public interface M {        String GET_NAME = "01";    }
public interface B { String SAY_HELLO = "01"; }}
复制代码


通过对具体的接口进行错误码定义,就可以通过错误码找到对应的接口。


3.2 如果使用错误码


​ 我们光定义好了错误码还是没有太大作用的,当生产环境出现问题的时候,不方便我们第一时间找出出现问题的地方,所以我们需要前端配合起来将错误码展示出来。


​ 如何展示呢? 其实很简单,就是通过在 msg 后边,把错误代码括号起来。给一个 demo:系统繁忙,请稍后再试(000160012) ,我们通过前端展示的错误码000160012 就可以知道出现的问题是:00 号系统,01 号业务,600(系统异常,是兜底,还是放过),01 号业务的 12 号接口,这样,我们就可以快速定位到具体的代码中去,在配合日志,很快就可以揪出 bug 出现的地方,是不是很方便?


​ 哈哈, 不知道大家发现一个问题没? 这样虽然解决了快速定位接口错误的问题,但是没有给测试带来收益,因为目前来说,接口的返回状态都是 200,这样就无法区分出哪一个接口是具体的错误,所以我们还要针对这一种情况进行处理。处理的代码如下:


    /**     * 业务异常统一捕获     *     * @param req 请求     * @param e 业务异常     * @return 对应结果     */    @ExceptionHandler(value = BizException.class)    public ResponseEntity<Result<?>> businessExceptionHandler(HttpServletRequest req, BizException e) {        String errorCode = e.getCode() + "";      	// 处理会话超时情况        if (SystemStatusEnum.SESSION_TIMEOUT.getCode().equals(errorCode)) {            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Result.fail(errorCode, e.getMessage()));        }      	// 属于业务异常的系统异常        if (SystemStatusEnum.DEFAULT_ERR_CODE.getCode().equals(errorCode)) {            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.fail(errorCode, e.getMessage()));        }      	// 其他异常        errorCode = StringUtils.isBlank(errorCode) ? SystemStatusEnum.DEFAULT_ERR_CODE.getCode() : errorCode;        return ResponseEntity.ok(Result.fail(errorCode, e.getMessage()));    }
复制代码


我这个处理业务的方法,先说一下为什么要这么做。


  1. 因为是业务异常,是我们自己抛的,所以不用打印多余的堆栈日志。

  2. 处理登录超时,可以给出 HttpStatus=401,让前端请求一目了然,可以针对 HttpStatus 做出具体处理

  3. 当系统异常的时候,需要把此次请求 HttpStatus=500,前端一目了然。


通过返回ResponseEntity而不是简单的Result,就解决了测试困难的情况。 不要通过固定思维,返回 Result,局限了自己,致使每次返回的 HttpStatus = 200, 使测试情况复杂化。


4. 总结


这一次,自己通过总结,算是理解了错误码的具体用法,分享出来,有问题,欢迎大家讨论呀!


发布于: 2021 年 03 月 24 日阅读数: 13
用户头像

编号94530

关注

你的每一个点赞我都当成了喜欢 2020.04.29 加入

公众号: 星球x 欢迎大家关注,谢谢!

评论

发布
暂无评论
如何定义错误码