玩框架并行 WSClient 调用错误管理
play framework parallel WSClient calls error management
我有一个操作,其中我进行了三个并行的 HTTP 调用(对其他服务),然后我将响应的内容合并到一个文档中,最后将其发送回客户端。
这是代码的工作示例:
@Inject
WSClient wsc;
public CompletionStage<Result> getUrlData() throws Exception {
List<CompletionStage<WSResponse>> stages = new ArrayList<>();
stages.add(wsc.url("http://jsonplaceholder.typicode.com/posts/1").get());
stages.add(wsc.url("http://jsonplaceholder.typicode.com/posts/2").get());
stages.add(wsc.url("http://jsonplaceholder.typicode.com/posts/3").get());
return Futures
.sequence(stages)
.thenApply(responses -> {
StringBuilder builder = new StringBuilder("[");
responses.stream().forEach(response -> builder.append(response.getBody()).append(","));
builder.deleteCharAt(builder.length()-1).append("]");
return ok(builder.toString());
})
.exceptionally(ex -> ok("{\"error\": \"An error has occurred\"}"));
如果其中一项服务不可用(您可以模拟此行为,将其中一个 URL 的域名修改为不存在的域名),页面 returned 仅包含异常中包含的消息() 部分,而我需要 return 正确调用的内容加上未成功调用的错误消息。关于如何操作的任何提示?
我正在使用 Play 2.5.1。
谢谢,
安德里亚
基本上您只想为每个调用单独处理 .exceptionally(..)
。这样的事情应该有效:
- 为个人 URL 创建一个 returns
CompletionStage
的函数,合并您的错误处理(返回 JSON 的错误)
- 将其转换为要传递给
Futures.sequence
的完成阶段列表
顺便说一句,您可以使用 Jackson 的 ObjectMapper.createObjectNode()
和 ObjectMapper.createArrayNode()
:
以编程方式构建对象,从而使 JSON 操作更好一些
private static final ObjectMapper mapper = new ObjectMapper();
private CompletionStage<JsonNode> getDataFromUrl(String url) {
return wsc.url(url)
.get()
.thenApply(WSResponse::asJson)
.exceptionally(ex -> {
ObjectNode error = mapper.createObjectNode();
error.put("error", ex.getMessage());
return error;
});
}
public CompletionStage<Result> getUrlData() throws Exception {
List<String> urls = new ArrayList<>();
urls.add("http://jsonplaceholder.typicode.com/posts/1");
urls.add("http://jsonplaceholder.typicode.com/posts/2");
urls.add("http://jsonplaceholder.typicode.com/posts/3");
// Convert to a list of promises
List<CompletionStage<JsonNode>> stages = urls
.stream()
.map(this::getDataFromUrl)
.collect(Collectors.toList());
return Futures
.sequence(stages)
.thenApply(responses -> {
ArrayNode arrayNode = mapper.createArrayNode();
responses.stream().forEach(arrayNode::add);
return ok(arrayNode);
});
}
我有一个操作,其中我进行了三个并行的 HTTP 调用(对其他服务),然后我将响应的内容合并到一个文档中,最后将其发送回客户端。 这是代码的工作示例:
@Inject
WSClient wsc;
public CompletionStage<Result> getUrlData() throws Exception {
List<CompletionStage<WSResponse>> stages = new ArrayList<>();
stages.add(wsc.url("http://jsonplaceholder.typicode.com/posts/1").get());
stages.add(wsc.url("http://jsonplaceholder.typicode.com/posts/2").get());
stages.add(wsc.url("http://jsonplaceholder.typicode.com/posts/3").get());
return Futures
.sequence(stages)
.thenApply(responses -> {
StringBuilder builder = new StringBuilder("[");
responses.stream().forEach(response -> builder.append(response.getBody()).append(","));
builder.deleteCharAt(builder.length()-1).append("]");
return ok(builder.toString());
})
.exceptionally(ex -> ok("{\"error\": \"An error has occurred\"}"));
如果其中一项服务不可用(您可以模拟此行为,将其中一个 URL 的域名修改为不存在的域名),页面 returned 仅包含异常中包含的消息() 部分,而我需要 return 正确调用的内容加上未成功调用的错误消息。关于如何操作的任何提示?
我正在使用 Play 2.5.1。
谢谢, 安德里亚
基本上您只想为每个调用单独处理 .exceptionally(..)
。这样的事情应该有效:
- 为个人 URL 创建一个 returns
CompletionStage
的函数,合并您的错误处理(返回 JSON 的错误) - 将其转换为要传递给
Futures.sequence
的完成阶段列表
顺便说一句,您可以使用 Jackson 的 ObjectMapper.createObjectNode()
和 ObjectMapper.createArrayNode()
:
private static final ObjectMapper mapper = new ObjectMapper();
private CompletionStage<JsonNode> getDataFromUrl(String url) {
return wsc.url(url)
.get()
.thenApply(WSResponse::asJson)
.exceptionally(ex -> {
ObjectNode error = mapper.createObjectNode();
error.put("error", ex.getMessage());
return error;
});
}
public CompletionStage<Result> getUrlData() throws Exception {
List<String> urls = new ArrayList<>();
urls.add("http://jsonplaceholder.typicode.com/posts/1");
urls.add("http://jsonplaceholder.typicode.com/posts/2");
urls.add("http://jsonplaceholder.typicode.com/posts/3");
// Convert to a list of promises
List<CompletionStage<JsonNode>> stages = urls
.stream()
.map(this::getDataFromUrl)
.collect(Collectors.toList());
return Futures
.sequence(stages)
.thenApply(responses -> {
ArrayNode arrayNode = mapper.createArrayNode();
responses.stream().forEach(arrayNode::add);
return ok(arrayNode);
});
}