<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.vertx.howtos</groupId>
<artifactId>web-and-openapi-howto</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<vertx.version>4.0.0</vertx.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-stack-depchain</artifactId>
<version>${vertx.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-openapi</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.5.0</version>
<configuration>
<mainClass>io.vertx.howtos.openapi.APIVerticle</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
Create Vert.x Web Router from an OpenAPI document
This document will show you how to use your OpenAPI document to create a Vert.x Web Router that validates and extract incoming request parameters.
What you will build
You will build a Vert.x application that manages an in-memory list of pets and serves it through the Petstore API https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v3.0/petstore.yaml
Create a project
Here is the content of the pom.xml
file you should be using:
pom.xml
Load the OpenAPI document
Vert.x Web API Contract provides you RouterBuilder
, an object that helps you to build the Vert.x Web Router starting from the OpenAPI specification.
To load the specification into the start method of your Verticle:
RouterBuilder.create(this.vertx, "petstore.yaml")
.onSuccess(routerBuilder -> { (1)
// You can start building the router using routerBuilder
}).onFailure(cause -> { (2)
// Something went wrong during router factory initialization
startPromise.fail(cause);
});
1 | If the loading succeeds, you receive a ready to use instance of RouterBuilder , otherwise |
2 | you fail the deploy of the verticle |
Write the handlers
Now you can fit your business logic into the Route handlers using operation(operationId).handler()
.
For listPets
:
routerBuilder.operation("listPets").handler(routingContext ->
routingContext
.response() (1)
.setStatusCode(200)
.putHeader(HttpHeaders.CONTENT_TYPE, "application/json") (2)
.end(new JsonArray(getAllPets()).encode()) (3)
);
1 | Get the response object |
2 | Put Content-type: application/json header |
3 | Write the response with all pets |
For createPets
:
routerBuilder.operation("createPets").handler(routingContext -> {
RequestParameters params = routingContext.get(ValidationHandler.REQUEST_CONTEXT_KEY); (1)
JsonObject pet = params.body().getJsonObject(); (2)
addPet(pet);
routingContext
.response()
.setStatusCode(200)
.end(); (3)
});
1 | Get the parsed parameters container |
2 | Extract the parsed body |
3 | Write the 200 empty response |
For showPetById
:
routerBuilder.operation("showPetById").handler(routingContext -> {
RequestParameters params = routingContext.get("parsedParameters"); (1)
Integer id = params.pathParameter("petId").getInteger(); (2)
Optional<JsonObject> pet = getAllPets()
.stream()
.filter(p -> p.getInteger("id").equals(id))
.findFirst(); (3)
if (pet.isPresent())
routingContext
.response()
.setStatusCode(200)
.putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
.end(pet.get().encode()); (4)
else
routingContext.fail(404, new Exception("Pet not found")); (5)
});
1 | Get the parsed parameters container |
2 | Extract the parsed path parameter |
3 | Search the pet |
4 | If pet is present, write the pet in the response |
5 | If pet is absent, fail the routing context with 404 |
Get the router
Now we can generate the Router
and add the "Not Found" and "Bad Request" error handlers:
Router router = routerBuilder.createRouter(); (1)
router.errorHandler(404, routingContext -> { (2)
JsonObject errorObject = new JsonObject() (3)
.put("code", 404)
.put("message",
(routingContext.failure() != null) ?
routingContext.failure().getMessage() :
"Not Found"
);
routingContext
.response()
.setStatusCode(404)
.putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
.end(errorObject.encode()); (4)
});
router.errorHandler(400, routingContext -> {
JsonObject errorObject = new JsonObject()
.put("code", 400)
.put("message",
(routingContext.failure() != null) ?
routingContext.failure().getMessage() :
"Validation Exception"
);
routingContext
.response()
.setStatusCode(400)
.putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
.end(errorObject.encode());
});
server = vertx.createHttpServer(new HttpServerOptions().setPort(8080).setHost("localhost")); (5)
server.requestHandler(router).listen(); (6)
1 | Generate the Router from the RouterBuilder |
2 | Mount the 404 not found error handler |
3 | Create the error json object with exception message, if any |
4 | Write the response with the error object |
5 | Instantiate a Vert.x HttpServer |
6 | Mount the router on the HttpServer instance |
Complete code
You can find the complete source code of APIVerticle
on this how-to repo.
Running the application
The APIVerticle
already has a main
method, so it can be used as-is to:
-
create a
Vertx
context, then -
deploy
APIVerticle
.
You can run the application from:
-
your IDE, by running the
main
method from theAPIVerticle
class, or -
with Maven:
mvn compile exec:java
You can test your API using any command-line tool like curl
:
$ curl http://localhost:8080/pets [{"id":1,"name":"Fufi","tag":"ABC"},{"id":2,"name":"Garfield","tag":"ABC"},{"id":3,"name":"Puffa","tag":"ABC"}] $ curl http://localhost:8080/pets/3 {"id":3,"name":"Puffa","tag":"ABC"} $ curl http://localhost:8080/pets/5 {"code":404,"message":"Pet not found"} $ curl -X POST -H "Content-type: application/json" --data '{"id":4,"name":"Alan"}' http://localhost:8080/pets $ curl -X POST -H "Content-type: application/json" --data '{"id":4}' http://localhost:8080/pets {"code":400,"message":"$.name: is missing but it is required"} $ curl http://localhost:8080/pets [{"id":1,"name":"Fufi","tag":"ABC"},{"id":2,"name":"Garfield","tag":"ABC"},{"id":3,"name":"Puffa","tag":"ABC"},{"id":4,"name":"Alan"}]