Web modules

Vert.x Web API Service

Preview

Vert.x Web API Service helps you handling HTTP Requests using the Vert.x Event Bus.

Event Bus provides important features like load balancing and distribution of requests across different Vert.x instances. We encourage you to give a look at Event Bus documentation for more info.

This module gives you the ability to create a Web API Service, an event bus message consumer based on same concept of Vert.x service proxy. Then it provides an handler to proxy the request to these services.

Using Vert.x API Service

To use Vert.x API Service, add the following dependency to the dependencies section of your build descriptor:

  • Maven (in your pom.xml):

<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-codegen</artifactId>
 <version>4.5.11</version>
 <classifier>processor</classifier>
</dependency>
<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-web-api-service</artifactId>
 <version>4.5.11</version>
</dependency>
  • Gradle < 5 (in your build.gradle file):

dependencies {
 compile 'io.vertx:vertx-codegen:4.5.11:processor'
 compile 'io.vertx:vertx-web-api-service:4.5.11'
}
  • Gradle >= 5 (in your build.gradle file):

dependencies {
 annotationProcessor 'io.vertx:vertx-codegen:4.5.11:processor'
 annotationProcessor 'io.vertx:vertx-web-api-service:4.5.11'
 compile 'io.vertx:vertx-web-api-service:4.5.11'
}

You need to import vertx-codegen to trigger the code generation from annotated interfaces. If you need only the RouteToEBServiceHandler, you don’t need it

If you want to use the interface you write in different languages, you will need to add the language dependency such as vertx-lang-groovy for Groovy.

Proxy an HTTP Request to a Web API Service

To proxy a request to the event bus you can use RouteToEBServiceHandler. This handler sends some data extracted from RoutingContext inside a ServiceRequest and expects a ServiceResponse as reply.

router
  .get("/hello")
  .handler(validationHandler)
  .handler(
    RouteToEBServiceHandler
      .build(eventBus, "greeters.myapplication", "hello")
  );

You can also define the DeliveryOptions that will be used each time a message is sent through the event bus:

router
  .get("/hello")
  .handler(validationHandler)
  .handler(
    RouteToEBServiceHandler
      .build(eventBus, "greeters.myapplication", "hello", new DeliveryOptions().setSendTimeout(1000))
  );
Before mounting the RouteToEBServiceHandler, you must mount a ValidationHandler that extracts the request parameters. Otherwise, no request parameters will be sent.

Define your Web API Service interface

We encourage you to read Service Proxy documentation before going further

Let’s assume we have defined two different routes in our Router as follows:

router.get("/api/transactions")
  .handler(
    ValidationHandlerBuilder.create(schemaParser)
      .queryParameter(optionalParam("from", stringSchema()))
      .queryParameter(optionalParam("to", stringSchema()))
      .build()
  ).handler(
    RouteToEBServiceHandler.build(eventBus, "transactions.myapplication", "getTransactionsList")
  );
router.post("/api/transactions")
  .handler(
    ValidationHandlerBuilder.create(schemaParser)
      .body(json(objectSchema()))
      .build()
  ).handler(
    RouteToEBServiceHandler.build(eventBus, "transactions.myapplication", "putTransaction")
  );

GET /api/transactions receives two optional query parameters as input: from and to. PUT /api/transactions receives a JsonObject as request body

Now we can build the interface TransactionService that handles those endpoints. For each endpoint you need to write a method with name corresponding to action specified when you build the RouteToEBServiceHandler. There are a couple of rules to follow for method parameters:

  • Last parameter must have type Handler<AsyncResult<ServiceResponse>>

  • Second to last parameter must have type ServiceRequest

  • All parameters from first to second to last (excluded) are extracted from RequestParameters with specified type automatically, but they need to respect service proxy restrictions

A request parameter is identified only by the name of the method parameter and the special body method parameter name is used to extract the body of the request.

For example:

@WebApiServiceGen
interface TransactionService {
 void getTransactionsList(String from, String to, ServiceRequest context, Handler<AsyncResult<ServiceResponse>> resultHandler);
 void putTransaction(JsonObject body, ServiceRequest context, Handler<AsyncResult<ServiceResponse>> resultHandler);
}

When you receive a request at TransactionService#getTransactionsList the generated service handler will automatically extract from and to parameter (if present) from ServiceRequest. In TransactionService#putTransaction we use the body parameter name to extract the json body.

The service handler is also capable to translate JsonObject to Vert.x data objects automatically, for example if you have a Transaction data object that matches the json schema above, you can rewrite the putTransaction signature as:

void putTransaction(Transaction body, ServiceRequest context, Handler<AsyncResult<ServiceResponse>> resultHandler);

You can also use RequestParameter to extract parameters, like:

void putTransaction(RequestParameter body, ServiceRequest context, Handler<AsyncResult<ServiceResponse>> resultHandler);

We encourage to extract with RequestParameter type the parameters that uses json schema allOf/anyOf/oneOf/not keywords because the extraction can produce undefined behaviours.

When working with DataObjects by default base64 strings are handled with the base64url alphabet, while OpenAPI does not mandate this alphabet so it assumes basic. To force a DataObject to use a specific alphabet this can be configured in the @DataObject annotation.

Implement your Web API Service

Now you can implement your service. Remember that ServiceRequest object contains headers and parameters maps.

To write the request you must call the resultHandler with an ServiceResponse. To create an instance of ServiceResponse you can use some handy methods like ServiceResponse.completedWithJson or ServiceResponse.completedWithPlainText

For example the implementation of TransactionService#getTransactionsList looks like:

resultHandler.handle(
  Future.succeededFuture(
    ServiceResponse.completedWithJson(new JsonArray())
  )
);

Or when it fails:

resultHandler.handle(
  Future.failedFuture(
    new HttpException(555, "Something bad happened")
  )
);

The ServiceRequest data object

ServiceRequest it’s a serializable version of RoutingContext, but It doesn’t contain all data of RoutingContext. It transports to your service:

  • getHeaders: Headers of the request

  • getParams: Contains routingContext.get("parsedParameters")

  • getUser: Contains routingContext.user().principal(), null if no user is authenticated

  • getExtra: Contains an extra configurable payload

You can configure a lambda that builds the extra payload with extraPayloadMapper

The ServiceResponse data object

ServiceResponse is composed by:

  • Headers of the response

  • Status code/Status message

  • Body as a payload. If you don’t set the payload/set as null no body will be sent

Expose your Web API Service

Now you can register your service to event bus:

TransactionService transactionService = new TransactionServiceImpl();

// Mount the service on the event bus
ServiceBinder transactionServiceBinder = new ServiceBinder(vertx);
transactionServiceBinder
  .setAddress("transactions.myapplication")
  .register(TransactionService.class, transactionService);

For more info on how to expose your service look at Vert.x service proxy documentation