= Vert.x Web Validation
:toc: left

Vert.x Web Validation helps you parse and validate parameters and bodies of the incoming requests.

You can:

* Parse and validate request parameters, serialized and exploded too
* Parse and validate request bodies, including json and forms
* Configure request predicates
* Allow different bodies in the same route and consistently parse and validate it
* Define custom rules to parse and validate
* Manage the parsing and validation failures

It uses https://vertx.io/docs/vertx-json-schema/java/[Vert.x Json Schema] to define schemas of your request parameters/bodies.

== Using Vert.x Web Validation

To use Vert.x Web Validation, add the following dependency to the _dependencies_ section of your build descriptor:

* Maven (in your `pom.xml`):

[source,xml,subs="+attributes"]
----
<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-web-validation</artifactId>
  <version>5.0.2</version>
</dependency>
----

* Gradle (in your `build.gradle` file):

[source,groovy,subs="+attributes"]
----
dependencies {
  compile 'io.vertx:vertx-web-validation:5.0.2'
}
----

== Without Vert.x Web Validation

When you receive an HTTP request, you usually need to perform parsing and validation of parameters and body of the request:

[source,java]
----
router
  .get("/user")
  .handler(routingContext -> {
    // Retrieve aParam
    String aParamUnparsed = routingContext.queryParam("aParam").get(0);
    if (aParamUnparsed == null) {
      routingContext.fail(400);
      return;
    }
    // Parse aParam
    int aParam;
    try {
      aParam = Integer.parseInt(aParamUnparsed);
    } catch (NumberFormatException e) {
      routingContext.fail(400, e);
      return;
    }
    // Check if aParam is maximum 100
    if (aParam > 100) {
      routingContext.fail(400);
      return;
    }

    // aParam is ready, now we can focus on
    // Business logic to process the request
  });
----

Vert.x Web Validation provides an easy to use API to build an handler that performs parsing and validation of the request:

[source,java]
----
router
  .get("/user")
  .handler(
    ValidationHandlerBuilder
      .create(schemaRepository)
      .queryParameter(param(
        "aParam",
        intSchema().with(maximum(100))
      ))
      .build()
  )
  .handler(routingContext -> {
    RequestParameters parameters =
      routingContext.get(ValidationHandler.REQUEST_CONTEXT_KEY);
    int aParam = parameters.queryParameter("aParam").getInteger();
    // Business logic to process the request
  });
----

== Creating the `ValidationHandler`

This module provides an easy to use builder API to create your `link:../../apidocs/io/vertx/ext/web/validation/ValidationHandler.html[ValidationHandler]`, the `link:../../apidocs/io/vertx/core/Handler.html[Handler]` that performs the parsing and validation of the request.
To create this builder use `link:../../apidocs/io/vertx/ext/web/validation/builder/ValidationHandlerBuilder.html#create-io.vertx.json.schema.SchemaRepository-[ValidationHandlerBuilder.create]`.

=== Defining parameters

You can define parameters located in four different locations of your request: query, cookie, header, path.

Every parameter is represented by a `link:../../apidocs/io/vertx/ext/web/validation/impl/parameter/ParameterProcessor.html[ParameterProcessor]`, that you can easily create with methods provided in `link:../../apidocs/io/vertx/ext/web/validation/builder/Parameters.html[Parameters]`:

[source,java]
----
ValidationHandlerBuilder
  .create(schemaRepository)
  .pathParameter(Parameters.param("myPathParam", stringSchema()))
  .queryParameter(Parameters.optionalParam("myQueryParam", intSchema()));
----

=== Defining custom error messages
Error messages can be customized:

* `link:../../apidocs/io/vertx/ext/web/validation/impl/parameter/ParameterProcessor.html#validationErrorMessage-java.lang.String-[validationErrorMessage]`
* `link:../../apidocs/io/vertx/ext/web/validation/impl/parameter/ParameterProcessor.html#parsingErrorMessage-java.lang.String-[parsingErrorMessage]`
* `link:../../apidocs/io/vertx/ext/web/validation/impl/parameter/ParameterProcessor.html#missingParameterErrorMessage-java.lang.String-[missingParameterErrorMessage]`

Note that all these methods requires a schema that validator can use to perform the validation.
The schema is also used to infer the correct parser

While header and path parameters allows only simple parameters, query and cookie allows complex parameters like exploded and deep object:

[source,java]
----
ValidationHandlerBuilder
  .create(schemaRepository)
  .queryParameter(Parameters.explodedParam(
    "myArray",
    arraySchema().items(stringSchema())
  ))  // Accepts myArray=item1&myArray=item2
  .queryParameter(Parameters.deepObjectParam(
    "myDeepObject",
    objectSchema()
      .property("name", stringSchema())
  )); // Accepts myDeepObject[name]=francesco
----

For more info on all available parameters, look at `link:../../apidocs/io/vertx/ext/web/validation/builder/Parameters.html[Parameters]` documentation.

=== Defining request bodies

Every body type is represented by a `link:../../apidocs/io/vertx/ext/web/validation/impl/parameter/ParameterProcessor.html[ParameterProcessor]` and matches with request body using `Content-type` header.
You can define one or multiple bodies that the `ValidationHandler` should manage.
If no matching body processor is found, the validation **won't** fail unless you specified the body required predicate explained below

You can easily create these processor with methods provided in `link:../../apidocs/io/vertx/ext/web/validation/builder/Bodies.html[Bodies]`:

[source,java]
----
ObjectSchemaBuilder bodySchemaBuilder = objectSchema()
  .property("username", stringSchema())
  .property("password", stringSchema());
ValidationHandlerBuilder
  .create(schemaRepository)
  .body(Bodies.json(bodySchemaBuilder))
  .body(Bodies.formUrlEncoded(bodySchemaBuilder));
----

In this example the `ValidationHandler` will be able to manage two different body types that consistently parse and validate.
In particular the form body will be converted to a json object.
When you retrieve the parsed result, you don't need to care if the request body was a form or a json

For more info on all available body processors, look at `link:../../apidocs/io/vertx/ext/web/validation/builder/Bodies.html[Bodies]` documentation.

=== Defining request predicates

You can define request predicates in `ValidationHandler` with `link:../../apidocs/io/vertx/ext/web/validation/RequestPredicate.html[RequestPredicate]`.
For example, to define a "request body required" predicate:

[source,java]
----
ValidationHandlerBuilder
  .create(schemaRepository)
  .predicate(RequestPredicate.BODY_REQUIRED);
----

=== Building the `ValidationHandler`

After you configured all parameters, bodies and request predicates, you can build the `ValidationHandler`:

[source,java]
----
router
  .get("/user")
  .handler(
    ValidationHandlerBuilder
      .create(schemaRepository)
      .build()
  );
----

== Using the parsed parameters and body

The `ValidationHandler` will place the parsed values into `link:../../apidocs/io/vertx/ext/web/RoutingContext.html[RoutingContext]`:

[source,java]
----
router
  .get("/user")
  .handler(
    ValidationHandlerBuilder
      .create(schemaRepository)
      .queryParameter(Parameters.explodedParam(
        "myArray",
        arraySchema().items(stringSchema())
      ))
      .body(Bodies.json(objectBodySchemaBuilder))
      .body(Bodies.formUrlEncoded(objectBodySchemaBuilder))
      .build()
  ).handler(routingContext -> {
    RequestParameters parameters =
      routingContext.get(ValidationHandler.REQUEST_CONTEXT_KEY);
    JsonArray myArray = parameters.queryParameter("myArray").getJsonArray();
    JsonObject body = parameters.body().getJsonObject();
  });
----

== Manage the failures

Every time a `ValidationHandler` encounters both a parsing or a validation failure, it fails the `RoutingContext` with 400 status code and an instance of a subclass of `link:../../apidocs/io/vertx/ext/web/validation/BadRequestException.html[BadRequestException]` as cause.
To learn how to manage failures, look at https://vertx.io/docs/vertx-web/java/#_error_handling[Vert.x Web doc] and `link:../../apidocs/io/vertx/ext/web/Router.html#errorHandler-int-io.vertx.core.Handler-[errorHandler]` method.

The possible subclasses of `link:../../apidocs/io/vertx/ext/web/validation/BadRequestException.html[BadRequestException]` are:

* `link:../../apidocs/io/vertx/ext/web/validation/ParameterProcessorException.html[ParameterProcessorException]`: To manage a parameter failure
* `link:../../apidocs/io/vertx/ext/web/validation/BodyProcessorException.html[BodyProcessorException]`: To manage a body failure
* `link:../../apidocs/io/vertx/ext/web/validation/RequestPredicateException.html[RequestPredicateException]`: To manage a request predicate failure

For example:

[source,java]
----
router.errorHandler(400, routingContext -> {
  if (routingContext.failure() instanceof BadRequestException) {
    if (routingContext.failure() instanceof ParameterProcessorException) {
      // Something went wrong while parsing/validating a parameter
    } else if (routingContext.failure() instanceof BodyProcessorException) {
      // Something went wrong while parsing/validating the body
    } else if (routingContext.failure() instanceof RequestPredicateException) {
      // A request predicate is unsatisfied
    }
  }
});
----

`BadRequestException` also provides an handy method called `link:../../apidocs/io/vertx/ext/web/validation/BadRequestException.html#toJson--[toJson]` that converts the exception to a Json

Note that the `ValidationHandler` is designed as _fail-fast_, so as soon as an error is encountered, the `ValidationHandler` will fail the `RoutingContext`