# Vert.x Web Client

Vert.x Web Client is an asynchronous HTTP and HTTP/2 client.

The Web Client makes easy to do HTTP request/response interactions with a web server, and provides advanced features like:

- Json body encoding / decoding

- request/response pumping

- request parameters

- unified error handling

- form submissions

The web client does not deprecate the Vert.x Core `HttpClient`, indeed it is based on this client and inherits its configuration and great features like pooling, HTTP/2 support, pipelining support, etc…​ The `HttpClient` should be used when fine grained control over the HTTP requests/responses is necessary.

The web client does not provide a WebSocket API, the Vert.x Core `HttpClient` should be used.

## Using the web client

To use Vert.x Web Client, 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-client-scala_2.12</artifactId>
      <version>3.4.2</version>
    </dependency>

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

    dependencies {
      compile 'io.vertx:vertx-web-client-scala_2.12:3.4.2'
    }

## Re-cap on Vert.x core HTTP client

Vert.x Web Client uses the API from Vert.x core, so it’s well worth getting familiar with the basic concepts of using `HttpClient` using Vert.x core, if you’re not already.

## Creating a web client

You create an `WebClient` instance with default options as follows

    var client = WebClient.create(vertx)

If you want to configure options for the client, you create it as follows

    var options = WebClientOptions()
      .setUserAgent("My-App/1.2.3")

    options.setKeepAlive(false)
    var client = WebClient.create(vertx, options)

Web Client options inherit Http Client options so you can set any one of them.

If your already have an HTTP Client in your application you can also reuse it

    var client = WebClient.wrap(httpClient)

## Making requests

### Simple requests with no body

Often, you’ll want to make HTTP requests with no request body. This is usually the case with HTTP GET, OPTIONS and HEAD requests

    var client = WebClient.create(vertx)

    // Send a GET request
    client.get(8080, "myserver.mycompany.com", "/some-uri").sendFuture().onComplete{
      case Success(result) => {
        // Obtain response
        var response = result

        println(s"Received response with status code${response.statusCode()}")
      }
      case Failure(cause) => {
        println(s"$cause")
      }
    }

    // Send a HEAD request
    client.head(8080, "myserver.mycompany.com", "/some-uri").sendFuture().onComplete{
      case Success(result) => {
        // Obtain response
        var response = result

        println(s"Received response with status code${response.statusCode()}")
      }
      case Failure(cause) => {
        println(s"$cause")
      }
    }

You can add query parameters to the request URI in a fluent fashion

    Code not translatable

Any request URI parameter will pre-populate the request

    var request = client.get(8080, "myserver.mycompany.com", "/some-uri?param1=param1_value&param2=param2_value")

    // Add param3
    request.addQueryParam("param3", "param3_value")

    // Overwrite param2
    request.setQueryParam("param2", "another_param2_value")

Setting a request URI discards existing query parameters

    var request = client.get(8080, "myserver.mycompany.com", "/some-uri")

    // Add param1
    request.addQueryParam("param1", "param1_value")

    // Overwrite param1 and add param2
    request.uri("/some-uri?param1=param1_value&param2=param2_value")

### Writing request bodies

When you need to make a request with a body, you use the same API and call then `sendXXX` methods that expects a body to send.

Use `sendBuffer` to send a buffer body

    // Send a buffer to the server using POST, the content-length header will be set for you
    client.post(8080, "myserver.mycompany.com", "/some-uri").sendBufferFuture(buffer).onComplete{
      case Success(result) => {
        // Ok
      }
      case Failure(cause) => println("Failure")
    }

Sending a single buffer is useful but often you don’t want to load fully the content in memory because it may be too large or you want to handle many concurrent requests and want to use just the minimum for each request. For this purpose the web client can send `ReadStream<Buffer>` (e.g a `AsyncFile` is a ReadStream<Buffer>`) with the `sendStream` method

    Code not translatable

The web client takes care of setting up the transfer pump for you. Since the length of the stream is not know the request will use chunked transfer encoding .

When you know the size of the stream, you shall specify before using the `content-length` header

    fs.openFuture("content.txt", OpenOptions()).onComplete{
      case Success(result) => {
        var fileStream = result

        var fileLen = "1024"

        // Send the file to the server using POST
        client.post(8080, "myserver.mycompany.com", "/some-uri").putHeader("content-length", fileLen).sendStreamFuture(fileStream).onComplete{
          case Success(result) => {
            // Ok
          }
          case Failure(cause) => println("Failure")
        }
      }
      case Failure(cause) => println("Failure")
    }

The POST will not be chunked.

#### Json bodies

Often you’ll want to send Json body requests, to send a `JsonObject` use the `sendJsonObject`

    client.post(8080, "myserver.mycompany.com", "/some-uri").sendJsonObjectFuture(new io.vertx.core.json.JsonObject().put("firstName", "Dale").put("lastName", "Cooper")).onComplete{
      case Success(result) => {
        // Ok
      }
      case Failure(cause) => println("Failure")
    }

In Java, Groovy or Kotlin, you can use the `sendJson` method that maps a POJO (Plain Old Java Object) to a Json object using `Json.encode` method

    client.post(8080, "myserver.mycompany.com", "/some-uri").sendJsonFuture(new examples.WebClientExamples.User("Dale", "Cooper")).onComplete{
      case Success(result) => {
        // Ok
      }
      case Failure(cause) => println("Failure")
    }

Note | the `Json.encode` uses the Jackson mapper to encode the object to Json.

#### Form submissions

You can send http form submissions bodies with the `sendForm` variant.

    var form = MultiMap.caseInsensitiveMultiMap()
    form.set("firstName", "Dale")
    form.set("lastName", "Cooper")

    // Submit the form as a form URL encoded body
    client.post(8080, "myserver.mycompany.com", "/some-uri").sendFormFuture(form).onComplete{
      case Success(result) => {
        // Ok
      }
      case Failure(cause) => println("Failure")
    }

By default the form is submitted with the `application/x-www-form-urlencoded` content type header. You can set the `content-type` header to `multipart/form-data` instead

    var form = MultiMap.caseInsensitiveMultiMap()
    form.set("firstName", "Dale")
    form.set("lastName", "Cooper")

    // Submit the form as a multipart form body
    client.post(8080, "myserver.mycompany.com", "/some-uri").putHeader("content-type", "multipart/form-data").sendFormFuture(form).onComplete{
      case Success(result) => {
        // Ok
      }
      case Failure(cause) => println("Failure")
    }

Note | at the moment multipart files are not supported, it will likely be supported in a later revision of the API.

### Writing request headers

You can write headers to a request using the headers multi-map as follows:

    var request = client.get(8080, "myserver.mycompany.com", "/some-uri")
    var headers = request.headers()
    headers.set("content-type", "application/json")
    headers.set("other-header", "foo")

The headers are an instance of `MultiMap` which provides operations for adding, setting and removing entries. Http headers allow more than one value for a specific key.

You can also write headers using putHeader

    var request = client.get(8080, "myserver.mycompany.com", "/some-uri")
    request.putHeader("content-type", "application/json")
    request.putHeader("other-header", "foo")

### Reusing requests

The `send` method can be called multiple times safely, making it very easy to configure and reuse `HttpRequest` objects

    var get = client.get(8080, "myserver.mycompany.com", "/some-uri")
    get.sendFuture().onComplete{
      case Success(result) => {
        // Ok
      }
      case Failure(cause) => println("Failure")
    }

    // Same request again
    get.sendFuture().onComplete{
      case Success(result) => {
        // Ok
      }
      case Failure(cause) => println("Failure")
    }

Beware though that `HttpRequest` instances are mutable. Therefore you should call the `copy` method before modifying a cached instance.

    var get = client.get(8080, "myserver.mycompany.com", "/some-uri")
    get.sendFuture().onComplete{
      case Success(result) => {
        // Ok
      }
      case Failure(cause) => println("Failure")
    }

    // The "get" request instance remains unmodified
    get.copy().putHeader("a-header", "with-some-value").sendFuture().onComplete{
      case Success(result) => {
        // Ok
      }
      case Failure(cause) => println("Failure")
    }

### Timeouts

You can set a timeout for a specific http request using `timeout`.

    client.get(8080, "myserver.mycompany.com", "/some-uri").timeout(5000).sendFuture().onComplete{
      case Success(result) => {
        // Ok
      }
      case Failure(cause) => {
        println(s"$cause")
      }
    }

If the request does not return any data within the timeout period an exception will be passed to the response handler.

## Handling http responses

When the web client sends a request you always deal with a single async result `HttpResponse`.

On a success result the callback happens after the response has been received

    client.get(8080, "myserver.mycompany.com", "/some-uri").sendFuture().onComplete{
      case Success(result) => {

        var response = result

        println(s"Received response with status code${response.statusCode()}")
      }
      case Failure(cause) => {
        println(s"$cause")
      }
    }

Warning | responses are fully buffered, use `BodyCodec.pipe` to pipe the response to a write stream

### Decoding responses

By default the web client provides an http response body as a `Buffer` and does not apply any decoding.

Custom response body decoding can be achieved using `BodyCodec`:

- Plain String

- Json object

- Json mapped POJO

- `WriteStream`

A body codec can decode an arbitrary binary data stream into a specific object instance, saving you the decoding step in your response handlers.

Use `BodyCodec.jsonObject` To decode a Json object:

    client.get(8080, "myserver.mycompany.com", "/some-uri").as(BodyCodec.jsonObject()).sendFuture().onComplete{
      case Success(result) => {
        var response = result

        var body = response.body()

        println(s"Received response with status code${response.statusCode()} with body ${body}")
      }
      case Failure(cause) => {
        println(s"$cause")
      }
    }

In Java, Groovy or Kotlin, custom Json mapped POJO can be decoded

    client.get(8080, "myserver.mycompany.com", "/some-uri").as(BodyCodec.json(examples.WebClientExamples.User.class)).sendFuture().onComplete{
      case Success(result) => {
        var response = result

        var user = response.body()

        println(s"Received response with status code${response.statusCode()} with body ${user.getFirstName()} ${user.getLastName()}")
      }
      case Failure(cause) => {
        println(s"$cause")
      }
    }

When large response are expected, use the `BodyCodec.pipe`. This body codec pumps the response body buffers to a `WriteStream` and signals the success or the failure of the operation in the async result response

    client.get(8080, "myserver.mycompany.com", "/some-uri").as(BodyCodec.pipe(writeStream)).sendFuture().onComplete{
      case Success(result) => {

        var response = result

        println(s"Received response with status code${response.statusCode()}")
      }
      case Failure(cause) => {
        println(s"$cause")
      }
    }

Finally if you are not interested at all by the response content, the `BodyCodec.none` simply discards the entire response body

    client.get(8080, "myserver.mycompany.com", "/some-uri").as(BodyCodec.none()).sendFuture().onComplete{
      case Success(result) => {

        var response = result

        println(s"Received response with status code${response.statusCode()}")
      }
      case Failure(cause) => {
        println(s"$cause")
      }
    }

When you don’t know in advance the content type of the http response, you can still use the `bodyAsXXX()` methods that decode the response to a specific type

    client.get(8080, "myserver.mycompany.com", "/some-uri").sendFuture().onComplete{
      case Success(result) => {

        var response = result

        // Decode the body as a json object
        var body = response.bodyAsJsonObject()

        println(s"Received response with status code${response.statusCode()} with body ${body}")
      }
      case Failure(cause) => {
        println(s"$cause")
      }
    }

Warning | this is only valid for the response decoded as a buffer.

### Handling 30x redirections

By default the client follows redirections, you can configure the default behavior in the `WebClientOptions`:

    // Change the default behavior to not follow redirects
    var client = WebClient.create(vertx, WebClientOptions()
      .setFollowRedirects(false)
    )

The client will follow at most `16` requests redirections, it can be changed in the same options:

    // Follow at most 5 redirections
    var client = WebClient.create(vertx, WebClientOptions()
      .setMaxRedirects(5)
    )

## Using HTTPS

Vert.x web client can be configured to use HTTPS in exactly the same way as the Vert.x `HttpClient`.

You can specify the behavior per request

    client.get(443, "myserver.mycompany.com", "/some-uri").ssl(true).sendFuture().onComplete{
      case Success(result) => {
        // Obtain response
        var response = result

        println(s"Received response with status code${response.statusCode()}")
      }
      case Failure(cause) => {
        println(s"$cause")
      }
    }

Or using create methods with absolute URI argument

    client.getAbs("https://myserver.mycompany.com:4043/some-uri").sendFuture().onComplete{
      case Success(result) => {
        // Obtain response
        var response = result

        println(s"Received response with status code${response.statusCode()}")
      }
      case Failure(cause) => {
        println(s"$cause")
      }
    }