Java 为未考虑的业务异常返回用户友好的error message

背景

项目早期底层方法捕获了能考虑到的业务异常,但即便如此,生产环境还是会产生各种难以预料的结果,所以在上层调用的时候,这些异常就进入了Exception中,返回到UI上就是“System error”

try
{

}
catch(bizException1 | bizException2)
{

}
catch(Exception)
{
  //log
  //return "System error"
}

这样不明所以的error message 会让用户很困扰,他们不知道发生了什么,也不知道该如何再操作了。
所以我们需要把有用的信息返回给用户,你可能说了,这还不简单,Exception.getMessage()中就有异常的信息。
但是,这个信息又过于“程序”了,想象一下,用户看到一个索引越界的message同样的还是不友好,和之前的“System error”并没有多大的区别。

我们需要给客户返回业务相关的错误信息,比如,在执行某个操作的时候出现了错误。
具体以当前自动化测试系统来说,如果出现了未考虑的业务异常,我们期望给用户返回“在跳转用户中心的时候出现了错误:在点击跳转按钮的时候出现了错误”。

解决方案

那我们总不能把所有的底层方法一个一个的全用try catch块给包起来吧,毕竟这种意料之外的业务异常还是少数。
那就思路就比较简单了,我们只需要对底层方法进行描述,出现异常的时候,通过反射拿到描述信息,进行拼接,就能得到用户友好的error message了。

注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MyAction {
    String name();
}

使用注解

@MyAction(name = "Navigate to xxx page")
public XXX navigateToUserCenterPage()  {
    
}

通过注解获取出异常的方法信息

public static String getErrorMessageFromMethodAnnotations(Exception exception) {
    List<Method> methods = getCallStackMethods(exception);
    List<String> messageList = new ArrayList<>();
    for (Method method : methods) {
        if (!Proxy.isProxyClass(method.getDeclaringClass())) {
            return extractTestActionMessage(messageList, method);
        }
    }
}

private static List<Method> getCallStackMethods(Exception exception) {
    List<Method> methods = new ArrayList<>();

    if (null == exception) {

        return methods;
    }

    StackTraceElement[] stackTraces = exception.getStackTrace();
    if (ArrayUtils.isEmpty(stackTraces)) {

        return methods;
    }


    StackTraceElement stackTrace;
    for (int i = stackTraces.length - 1; i >= 0; i--) {
        stackTrace = stackTraces[i];

        Class<?> clazz;
        try {
            String className = stackTrace.getClassName();
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            continue;
        }

        Method[] classMethods;
        try {
            classMethods = clazz.getDeclaredMethods();
        } catch (SecurityException e) {
            continue;
        }

        String methodName = stackTrace.getMethodName();
        Method method = Arrays.stream(classMethods)
                .filter(m -> m.getName().equals(methodName))
                .findFirst()
                .orElse(null);
        if (null == method) {
            continue;
        }
        methods.add(method);
    }

    return methods;
}

private static void extractMyActionMessage(List<String> messageList, Method method) {
    TestAction testAction = MethodUtils.getAnnotation(method, MyAction.class, true, true);
    if (testAction != null) {
        String nameValue = testAction.name();
        if (StringUtils.isNotBlank(nameValue)) {
            messageList.add(nameValue.trim() + SPACE + FAILED);
        }
    }
}
posted @ 2023-12-11 20:36  talentzemin  阅读(23)  评论(0编辑  收藏  举报