Vert.x Web API Contract extends Vert.x Web to support OpenAPI 3, bringing to you a simple interface to build your router and mount security and validation handler.
If you are interested in building an application that routes API Requests to event bus, check out Vert.x Web API Service
To use Vert.x API Contract, add the following dependency to the dependencies section of your build descriptor:
Maven (in your pom.xml
):
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-api-contract</artifactId>
<version>3.6.2</version>
</dependency>
Gradle (in your build.gradle
file):
dependencies {
compile 'io.vertx:vertx-web-api-contract:3.6.2'
}
Vert.x provides a validation framework that will validate requests for you and will put results of validation inside a container. To define a HTTPRequestValidationHandler
:
require 'vertx-web-api-contract/http_request_validation_handler'
# Create Validation Handler with some stuff
validationHandler = VertxWebApiContract::HTTPRequestValidationHandler.create().add_query_param("parameterName", :INT, true).add_form_param_with_pattern("formParameterName", "a{4}", true).add_path_param("pathParam", :FLOAT)
Then you can mount your validation handler:
require 'vertx-web/body_handler'
# BodyHandler is required to manage body parameters like forms or json body
router.route().handler(&VertxWeb::BodyHandler.create().method(:handle))
router.get("/awesome/:pathParam").handler(&validationHandler.method(:handle)).handler() { |routingContext|
# Get Request parameters container
params = routingContext.get("parsedParameters")
# Get parameters
parameterName = params.query_parameter("parameterName").get_integer()
formParameterName = params.form_parameter("formParameterName").get_string()
pathParam = params.path_parameter("pathParam").get_float()
}.failure_handler() { |routingContext|
failure = routingContext.failure()
if (failure.class.name == 'Java::IoVertxExtWebApiValidation::ValidationException')
# Something went wrong during validation!
validationErrorMessage = failure.get_message()
end
}
If validation succeeds, It returns request parameters inside RequestParameters
, otherwise It will throw a ValidationException
Every parameter has a type validator, a class that describes the expected type of parameter.
A type validator validates the value, casts it in required language type and then loads it inside a RequestParameter
object. There are three ways to describe the type of your parameter:
There is a set of prebuilt types that you can use: ParameterType
You can instantiate a custom instance of prebuilt type validators using static methods of ParameterTypeValidator
and then load it into HTTPRequestValidationHandler
using functions ending with WithCustomTypeValidator
You can create your own ParameterTypeValidator
implementing ParameterTypeValidator
interface
Now you can handle parameter values:
params = routingContext.get("parsedParameters")
awesomeParameter = params.query_parameter("awesomeParameter")
if (awesomeParameter != nil)
if (!awesomeParameter.empty?())
# Parameter exists and isn't empty
# ParameterTypeValidator mapped the parameter in equivalent language object
awesome = awesomeParameter.get_integer()
else
# Parameter exists, but it's empty
end
else
# Parameter doesn't exist (it's not required)
end
As you can see, every parameter is mapped in respective language objects. You can also get a json body:
body = params.body()
if (body != nil)
jsonBody = body.get_json_object()
end
Vert.x allows you to use your OpenApi 3 specification directly inside your code using the design first approach. Vert.x-Web provides:
OpenAPI 3 compliant API specification validation with automatic*loading of external Json schemas**
Automatic request validation
Automatic mount of security validation handlers
Automatic 501 response for not implemented operations
Router factory to provide all these features to users
You can also use the community project slush-vertx
to generate server code from your OpenAPI 3 specification.
You can create your web service based on OpenAPI3 specification with OpenAPI3RouterFactory
.
This class, as name says, is a router factory based on your OpenAPI 3 specification.
OpenAPI3RouterFactory
is intended to give you a really simple user interface to use OpenAPI 3 support. It includes:
Async loading of specification and its schema dependencies
Mount path with operationId or with combination of path and HTTP method
Automatic request parameters validation
Automatic convert OpenAPI style paths to Vert.x style paths
Lazy methods: operations (combination of paths and HTTP methods) are mounted in declaration order inside specification
Automatic mount of security validation handlers
To create a new router factory, Use method OpenAPI3RouterFactory.create
.
As location It accepts absolute paths, local paths and local or remote URLs (HTTP or file protocol).
For example:
require 'vertx-web-api-contract/open_api3_router_factory'
VertxWebApiContract::OpenAPI3RouterFactory.create(vertx, "src/main/resources/petstore.yaml") { |ar_err,ar|
if (ar_err == nil)
# Spec loaded with success
routerFactory = ar
else
# Something went wrong during router factory initialization
exception = ar_err
end
}
You can also construct a router factory from a remote spec:
require 'vertx-web-api-contract/open_api3_router_factory'
VertxWebApiContract::OpenAPI3RouterFactory.create(vertx, "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore.yaml") { |ar_err,ar|
if (ar_err == nil)
# Spec loaded with success
routerFactory = ar
else
# Something went wrong during router factory initialization
exception = ar_err
end
}
Or, you can also access a private remote spec by passing one or more AuthorizationValue:
Code not translatable
You can also modify the behaviours of the router factory with RouterFactoryOptions
.
For example you can ask to router factory to mount the validation failure handler but to not mount the not implemented handler as follows:
routerFactory = ar.result()
# Create and mount options to router factory
options = {
'mountNotImplementedHandler' => true,
'mountValidationFailureHandler' => false
}
routerFactory.set_options(options)
Now load your first operation handlers. To load an handler use addHandlerByOperationId
. To load a failure handler use addFailureHandlerByOperationId
You can, of course,add multiple handlers to same operation*, without overwrite the existing ones.
For example:
routerFactory.add_handler_by_operation_id("awesomeOperation") { |routingContext|
params = routingContext.get("parsedParameters")
body = params.body()
jsonBody = body.get_json_object()
# Do something with body
}
routerFactory.add_failure_handler_by_operation_id("awesomeOperation") { |routingContext|
# Handle failure
}
Important
|
Add operations with operationId
Usage of combination of path and HTTP method is allowed, but it’s better to add operations handlers with operationId, for performance reasons and to avoid paths nomenclature errors
|
Now you can use parameter values as described above
A security handler is defined by a combination of schema name and scope. You can mount only one security handler for a combination. For example:
routerFactory.add_security_handler("security_scheme_name", &securityHandler)
You can of course use included Vert.x security handlers, for example:
require 'vertx-web/jwt_auth_handler'
routerFactory.add_security_handler("jwt_auth", &VertxWeb::JWTAuthHandler.create(jwtAuthProvider).method(:handle))
The router factory allows you to customize some behaviours during router generation with
RouterFactoryOptions
. Router factory can:
Mount a 501 Not Implemented
handler for operations where you haven’t mounted any handler
Mount a 400 Bad Request
handler that manages ValidationException
Mount the ResponseContentTypeHandler
handler when needed
Give a deeper look at RouterFactoryOptions
documentation
When you are ready, generate the router and use it:
router = routerFactory.get_router()
server = vertx.create_http_server({
'port' => 8080,
'host' => "localhost"
})
server.request_handler(&router.method(:handle)).listen()