Reputation: 2578
I have a Vertx application with a router endpoint:
router.route(HttpMethod.GET, Constants.ENDPOINT).blockingHandler(this::getItems);
This router calls a method, that is supposed to return a JSON object in the browser, or whatever client is calling this endpoint. The JSON object actually comes from a completely different service. I am using Vert.x's WebClient
library to call this service.
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();
}
The data I get from my-site.com
is fine and displays in the console with my System.out command. The problem is that I cannot get it into response.write
.
Reading up, I see that this is related to futures. I don't really understand the concept, so I've been doing a lot of reading but cannot find any examples that fit my particular code.
How would I go about implementing futures so that the data I receive from my-site.com
gets put into my Json object (data
), and then can be used in response.write
?
Upvotes: 1
Views: 2015
Reputation: 2964
The Vert.x documentation about async coordination is very good and uses futures in the examples. Here is the way I would implement it using 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();
}
});
}
Upvotes: 1
Reputation: 79
In your impl data will be an empty JSON object because Webclient is async. You are writing the response to the client before the response from Webclient is ready.
Move the write into the webclient response and end the context there. E.g:
...
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();
...
Upvotes: 2