【问题标题】:Play Framework 2.3.1 Promise exception handling within inner classPlay Framework 2.3.1 Promise 内部类异常处理
【发布时间】:2014-06-26 15:01:15
【问题描述】:

在 WS API 请求中实现正确的异常处理的正确方法是什么?

一个例子:

F.Promise<List<String>> modulesPromise = WS.url(requestUrl).setAuth(apiUser, apiPassword).get().map(
            new F.Function<WSResponse, List<String>>() {
                public List<String> apply(WSResponse response) {
                    List<String> modules = new ArrayList<>();

                    JsonNode json = response.asJson();
                    for (JsonNode node : json) {
                        modules.add(node.get("name").toString().replace("\"", StringUtils.EMPTY));
                    }

                    return modules;
                }
            }
    );

    modulesPromise.recover(new F.Function<Throwable, List<String>>() {
        @Override
        public List<String> apply(Throwable throwable) throws Throwable {
            Logger.error("error", throwable);
            return new ArrayList<>();
        }
    });

如果内部类在这种情况下发生异常,则会进一步抛出异常。 .onFailure() 方法也存在同样的问题。这不是使用 try catch 块来捕获潜在异常的最佳方法吗? recover 和 onFailure 方法好像没什么用。

【问题讨论】:

    标签: java exception-handling error-handling playframework promise


    【解决方案1】:

    在您的情况下,recover 似乎对您没有多大好处。 recover 用于在 Promise 失败时提供默认值(例如,其中抛出异常)。

    在这种情况下,您正在对 List 进行操作,如果 name 不是 JsonNode 中的字段,则调用 node.get("name").toString() 将引发异常。这将导致Promise 完全失败,因此您的列表的其余部分将消失。您可以将它包装在 try/catch 中,但这不是必需的。

    由于node.get("name")返回null,如果没有name,那么你应该在操作之前检查null:

     for (JsonNode node : json) {
         if(node.get("name") != null)
             modules.add(node.get("name").toString().replace("\"", StringUtils.EMPTY));
     }
    

    所以答案实际上是取决于您的用例。通常,如果Promise 中发生异常,您希望它失败。然后您的其余代码可以适当地处理它。但是,如果可能发生通常不会影响最终结果的异常,则要么捕获它,要么以其他方式处理它,以防止它失败 Promise(在这种情况下检查 null)。

    【讨论】:

    • 感谢您的回答,但我的问题是另一个问题。网络服务器不可用导致的异常应该如何处理? F.Promise 中的 onFailure 方法是处理此类异常的正确方法吗?当它是 - 我如何使用它?还是在 try catch 块中执行我上面的问题中的代码块以提供正确且良好的异常处理?
    • 如果由于 Web 服务器不可用而引发异常,则 Promise 将失败,您应该允许这种情况发生。这就是Promise 有成功和失败状态的原因。通过将异常映射到实际结果,您可以 recover 将其转换为成功的Promise,但您不需要这样做。
    【解决方案2】:

    你是关闭。你所拥有的基本上是:

    promise = WS.get
        .map;
    promise.recover;
    

    你原本想要的是:

    promise = WS.get
        .map
        .recover;
    

    你的直觉是对的:虽然try/catch 同步处理异常,与当前执行的线程内联,但像recover 这样的方法在评估/履行这些承诺时异步处理异常。关键是每个promise都是独立的,那些返回promise的方法实际上是返回一个新promise。

    get 承诺返回 HTTP 调用的结果。 map 承诺获取该结果并从每个节点中提取“名称”。 recover 承诺如果存在异常链中的任何地方,它将返回一个新的空列表。但是在你的例子中,最后一个承诺没有在任何地方分配/使用,所以没有人在听/看/等待那个承诺;他们正在等待map,它可能会失败(本身或get)。请注意,如果出现异常,recover 代码仍然会执行,但没有人想要结果。

    是否使用recover 或让整个调用失败是一个单独的问题。例如,如果您正在编写来自其他几个调用的响应,也许您可​​以将失败的第三方调用替换为“不可用”,或者干脆省略不起作用的元素。

    虽然recover 返回一个新的承诺,但onFailure 是void。这就是您的示例所做的:在出现异常时做某事,但没有人依赖它。例如,假设您想在第三方呼叫失败时进行记录。您可以在 recover 中执行此操作 - 日志然后返回一个空列表 - 或者您可以单独执行此操作:recover 返回空列表,onFailure 日志。这样,功能更加可组合;再次强调,区分不同的 promise 很重要:

    promise = WS.get
        .map;
    promise.onFailure;
    promise = promise.recover;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-18
      • 2014-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-28
      相关资源
      最近更新 更多