如何定义错误码
Posted 编号94530
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何定义错误码相关的知识,希望对你有一定的参考价值。
好久没有写文章了,搞得不知道写啥,最近遇到了一个问题,决定把东西总结一下,记录下来。
1. 背景
最近开发,发现遇到了一个问题,那就是关于如何定义错误码。写代码写着写着迷糊了,突然发现不知道如何定义错误码,错误码对我们来说,到底有什么用?今天我结合自己的实现,来说一下如何定义错误码
2. 处理错误的方式
在Spring时代,spring提供的ControllerAdvice,RestControllerAdvice给我们集中处理异常提供了一种很好的解决方案。我们也经常用这样的方式来解决方案。但是,结合我们一部分常用的也给我带来了一部分烦恼,我来说一下为什么。
2.1 处理方案
在我们自己的代码中,经常会定义一种通用的返回结果,大多数都叫Result。代码如下:
@Data
@AllArgsConstructor
public class Result<T> implements Serializable
/**
* 状态码
*/
private Object code;
/***
* 说明信息
*/
private String message;
/**
* 是否成功
*/
private boolean success;
/**
* 返回数据
*/
private T data;
我们经常会用这个对象当做接口的公共返回,所以理所当然的,在定义公共异常处理逻辑的时候,也会写成如下的样子。代码如下:
@RestControllerAdvice
@Slf4j
public 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或者其他方式找异常的我们都太天真了。我顿悟是在一张图上,图上的内容大概如下。
接口 | 错误时处理方式 |
---|---|
A | 前端不做处理,不允许阻塞下单流程 |
F | 前端不做处理,不允许阻塞下单流程 |
G | 前端不做处理,不允许阻塞下单流程 |
B | 前端做兜里逻辑 |
W | 前端做兜里逻辑 |
C | 需要给出错误提示 |
看到这个图的第一反应,我觉得需要定义好多错误码,好麻烦,能不能给出一种公共的,然后让前端好处理一些。并且我想改变我的代码,不想让测试费劲的看哪个接口出问题,便于我自己找问题,也便于测试。而且我还想前端基于目前的代码,不要有太大的改动,就算有改动,没覆盖到的地方,在以后的迭代中进行修改。
思前想后,想到一个这样的处理逻辑,一下子处理的当前的困境。
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()));
我这个处理业务的方法,先说一下为什么要这么做。
- 因为是业务异常,是我们自己抛的,所以不用打印多余的堆栈日志。
- 处理登录超时,可以给出HttpStatus=401,让前端请求一目了然,可以针对HttpStatus做出具体处理
- 当系统异常的时候,需要把此次请求HttpStatus=500,前端一目了然。
通过返回ResponseEntity
而不是简单的Result
,就解决了测试困难的情况。 不要通过固定思维,返回Result,局限了自己,致使每次返回的HttpStatus = 200, 使测试情况复杂化。
4. 总结
这一次,自己通过总结,算是理解了错误码的具体用法,分享出来,有问题,欢迎大家讨论呀!
以上是关于如何定义错误码的主要内容,如果未能解决你的问题,请参考以下文章
ListView 自定义适配器对错误的项 OnScroll 应用操作