播放框架并行WSClient调用错误管理

时间:2016-04-06 17:09:57

标签: asynchronous playframework-2.0

我有一个动作,我在其中进行三次并行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的域名修改为不存在的URL),则返回的页面仅包含exceptionally()部分中包含的消息,而我需要返回正确调用的内容以及未成功调用的错误消息。有关如何做的任何提示吗?

我正在使用Play 2.5.1。

谢谢, 安德烈

1 个答案:

答案 0 :(得分:1)

基本上,您只想为每次通话单独处理.exceptionally(..)。这样的事情应该有效:

  1. 创建一个函数,为单个URL返回CompletionStage,并包含错误处理(返回错误的JSON)
  2. 将其转换为完成阶段列表,以传递给Futures.sequence
  3. 顺便说一句,你可以通过使用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);
                });
    }