Как использовать фьючерсы в маршрутизаторах Vertx с WebClient в Java

У меня есть приложение Vertx с конечной точкой маршрутизатора:

router.route(HttpMethod.GET, Constants.ENDPOINT).blockingHandler(this::getItems);

Этот маршрутизатор вызывает метод, который должен возвращать объект JSON в браузере или любой клиент, вызывающий эту конечную точку. На самом деле объект JSON происходит из совершенно другого сервиса. Я использую Vert.x WebClient библиотека для вызова этой услуги.

    private void getItems(RoutingContext routingContext) {
    HttpServerResponse response = routingContext.response();
    response.setChunked(true);
    response.putHeader("content-type", "text/plain");
    response.putHeader("Access-Control-Allow-Origin", "*");
    JsonObject data = new JsonObject();
    WebClient webClient = WebClient.create(vertx);
    webClient.post(80, "my-site.com", "/api/items")
        .as(BodyCodec.jsonArray())
        .putHeader("Accept", "application/json")
        .putHeader("Content-Type", "application/json")
        .sendJsonObject(new JsonObject().put("mutator", "*"), ar -> {
            if (ar.succeeded()) {
                HttpResponse<JsonArray> result = ar.result();
                JsonArray body = result.body();
                System.out.println(body);
                data.put("data", body.getJsonObject(0));
            } else {
                data.put("data", ar.cause().getMessage());
            }
        }); 
    response.write(data.encode());
    routingContext.response().end();
}

Данные, которые я получаю от my-site.com в порядке и отображается в консоли с моей командой System.out. Проблема в том, что я не могу получить это в response.write,

Читая, я вижу, что это связано с фьючерсами. Я не совсем понимаю эту концепцию, поэтому я много читал, но не могу найти примеров, подходящих для моего конкретного кода.

Как бы я пошел о реализации фьючерсов, чтобы данные, которые я получаю от my-site.com попадает в мой объект Json (data), а затем может быть использован в response.write?

2 ответа

Решение

В ваших данных impl будет пустой объект JSON, потому что Webclient является асинхронным. Вы пишете ответ клиенту до того, как ответ от Webclient будет готов.

Переместите запись в ответ веб-клиента и завершите контекст там. Например:

...

if (ar.succeeded()) { 
    HttpResponse<JsonArray> result = ar.result();
    JsonArray body = result.body();
    System.out.println(body); 
    data.put("data", body.getJsonObject(0));
} else { 
    data.put("data", ar.cause().getMessage());
}

response.write(data.encode());
routingContext.response().end();

...

Документация Vert.x об асинхронной координации очень хороша и использует примеры в примерах. Вот как я мог бы реализовать это, используя Vert.x Futures:

private void getItems(RoutingContext routingContext) {
        HttpServerResponse response = routingContext.response();
        response.setChunked(true);
        response.putHeader("content-type", "text/plain");
        response.putHeader("Access-Control-Allow-Origin", "*");
        // init a future that should hold a JsonObject result
        Future<JsonObject> future = Future.future();
        JsonObject data = new JsonObject();
        WebClient webClient = WebClient.create(vertx);
        webClient.post(80, "my-site.com", "/api/items")
                .as(BodyCodec.jsonArray())
                .putHeader("Accept", "application/json")
                .putHeader("Content-Type", "application/json")
                .sendJsonObject(new JsonObject().put("mutator", "*"), ar -> {
                    if (ar.succeeded()) {
                        HttpResponse<JsonArray> result = ar.result();
                        JsonArray body = result.body();
                        System.out.println(body);
                        data.put("data", body.getJsonObject(0));
                        // set future to be completed, with data object as its JsonObject result
                        future.complete(data);
                    } else {
                         data.put("data", ar.cause().getMessage());
                         future.complete(data);
                         // we can also set the future as failed and give it a Throwable
                        // future.fail(ar.cause());
                    }
                });
        // handle when the future is completed
        future.setHandler(jsonObjectAsyncResult -> {
            if(jsonObjectAsyncResult.succeeded()) {
                response.write(data.encode());
                routingContext.response().end();
            }
        });
    }
Другие вопросы по тегам