The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

Using the REST Client

This guide explains how to use the REST Client in order to interact with REST APIs. REST Client is the REST Client implementation compatible with Quarkus REST (formerly RESTEasy Reactive).

If your application uses a client and exposes REST endpoints, please use Quarkus REST for the server part.

先决条件

完成这个指南,你需要:

  • 大概15分钟

  • 编辑器

  • JDK 17+ installed with JAVA_HOME configured appropriately

  • Apache Maven 3.9.9

  • 如果你愿意的话,还可以选择使用Quarkus CLI

  • 如果你想构建原生可执行程序,可以选择安装Mandrel或者GraalVM,并正确配置(或者使用Docker在容器中进行构建)

解决方案

我们建议您按照下一节的说明逐步创建应用程序。然而,您可以直接转到已完成的示例。

克隆 Git 仓库: git clone https://github.com/quarkusio/quarkus-quickstarts.git ,或下载一个 存档

The solution is located in the rest-client-quickstart directory.

创建Maven项目

首先,我们需要一个新的工程项目。用以下命令创建一个新项目:

CLI
quarkus create app org.acme:rest-client-quickstart \
    --extension='rest-jackson,rest-client-jackson' \
    --no-code
cd rest-client-quickstart

创建Grade项目,请添加 --gradle 或者 --gradle-kotlin-dsl 参数。

For more information about how to install and use the Quarkus CLI, see the Quarkus CLI guide.

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.16.3:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=rest-client-quickstart \
    -Dextensions='rest-jackson,rest-client-jackson' \
    -DnoCode
cd rest-client-quickstart

创建Grade项目,请添加 -DbuildTool=gradle 或者 -DbuildTool=gradle-kotlin-dsl 参数。

For Windows users:

  • If using cmd, (don’t use backward slash \ and put everything on the same line)

  • If using Powershell, wrap -D parameters in double quotes e.g. "-DprojectArtifactId=rest-client-quickstart"

This command generates the Maven project with a REST endpoint and imports:

  • the rest-jackson extension for the REST server support. Use rest instead if you do not wish to use Jackson;

  • the rest-client-jackson extension for the REST client support. Use rest-client instead if you do not wish to use Jackson

If you already have your Quarkus project configured, you can add the rest-client-jackson extension to your project by running the following command in your project base directory:

CLI
quarkus extension add rest-client-jackson
Maven
./mvnw quarkus:add-extension -Dextensions='rest-client-jackson'
Gradle
./gradlew addExtension --extensions='rest-client-jackson'

这会在你的构建文件中添加以下内容:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-rest-client-jackson</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-rest-client-jackson")

Setting up the model

In this guide we will be demonstrating how to consume part of the REST API supplied by the stage.code.quarkus.io service. Our first order of business is to set up the model we will be using, in the form of a Extension POJO.

Create a src/main/java/org/acme/rest/client/Extension.java file and set the following content:

package org.acme.rest.client;

import java.util.List;

public class Extension {

    public String id;
    public String name;
    public String shortName;
    public List<String> keywords;

}

The model above is only a subset of the fields provided by the service, but it suffices for the purposes of this guide.

Create the interface

Using the REST Client is as simple as creating an interface using the proper Jakarta REST and MicroProfile annotations. In our case the interface should be created at src/main/java/org/acme/rest/client/ExtensionsService.java and have the following content:

package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import java.util.Set;

@Path("/extensions")
@RegisterRestClient
public interface ExtensionsService {

    @GET
    Set<Extension> getById(@QueryParam("id") String id);
}

The getById method gives our code the ability to get an extension by id from the Code Quarkus API. The client will handle all the networking and marshalling leaving our code clean of such technical details.

The purpose of the annotations in the code above is the following:

  • @RegisterRestClient allows Quarkus to know that this interface is meant to be available for CDI injection as a REST Client

  • @Path, @GET and @QueryParam are the standard Jakarta REST annotations used to define how to access the service

When the quarkus-rest-client-jackson extension is installed, Quarkus will use the application/json media type by default for most return values, unless the media type is explicitly set via @Produces or @Consumes annotations.

If you don’t rely on the JSON default, it is heavily recommended to annotate your endpoints with the @Produces and @Consumes annotations to define precisely the expected content-types. It will allow to narrow down the number of Jakarta REST providers (which can be seen as converters) included in the native executable.

The getById method above is a blocking call. It should not be invoked on the event loop. The Async Support section describes how to make non-blocking calls.

Query Parameters

The easiest way to specify a query parameter is to annotate a client method parameter with the @QueryParam or the @RestQuery. The @RestQuery is equivalent of the @QueryParam, but with optional name. Additionally, it can be also used to pass query parameters as a Map, which is convenient if parameters are not known in advance.

package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.reactive.RestQuery;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MultivaluedMap;
import java.util.Map;
import java.util.Set;

@Path("/extensions")
@RegisterRestClient(configKey = "extensions-api")
public interface ExtensionsService {

    @GET
    Set<Extension> getById(@QueryParam("id") String id);

    @GET
    Set<Extension> getByName(@RestQuery String name); (1)

    @GET
    Set<Extension> getByFilter(@RestQuery Map<String, String> filter); (2)

    @GET
    Set<Extension> getByFilters(@RestQuery MultivaluedMap<String, String> filters); (3)

}
1 Request query will include parameter with key name
2 Each Map entry represents exactly one query parameter
3 MultivaluedMap allows you to send array values

Using @ClientQueryParam

Another way to add query parameters to a request is to use @io.quarkus.rest.client.reactive.ClientQueryParam on either the REST client interface or a specific method of the interface. The annotation can specify the query parameter name while the value can either be a constant, a configuration property or it can be determined by invoking a method.

The following example shows the various possible usages:

@ClientQueryParam(name = "my-param", value = "${my.property-value}") (1)
public interface Client {
    @GET
    String getWithParam();

    @GET
    @ClientQueryParam(name = "some-other-param", value = "other") (2)
    String getWithOtherParam();

    @GET
    @ClientQueryParam(name = "param-from-method", value = "{with-param}") (3)
    String getFromMethod();

    default String withParam(String name) {
        if ("param-from-method".equals(name)) {
            return "test";
        }
        throw new IllegalArgumentException();
    }
}
1 By placing @ClientQueryParam on the interface, we ensure that my-param will be added to all requests of the client. Because we used the ${…​} syntax, the actual value of the parameter will be obtained using the my.property-value configuration property.
2 When getWithOtherParam is called, in addition to the my-param query parameter, some-other-param with the value of other will also be added.
3 when getFromMethod is called, in addition to the my-param query parameter, param-from-method with the value of test (because that’s what the withParam method returns when invoked with param-from-method) will also be added.

Note that if an interface method contains an argument annotated with @QueryParam, that argument will take priority over anything specified in any @ClientQueryParam annotation.

More information about this annotation can be found on the javadoc of @ClientQueryParam.

Form Parameters

Form parameters can be specified using @RestForm (or @FormParam) annotations:

package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.reactive.RestForm;

import jakarta.ws.rs.PORT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.core.MultivaluedMap;
import java.util.Map;
import java.util.Set;

@Path("/extensions")
@RegisterRestClient(configKey = "extensions-api")
public interface ExtensionsService {

    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    Set<Extension> postId(@FormParam("id") String id);

    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    Set<Extension> postName(@RestForm String name);

    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    Set<Extension> postFilter(@RestForm Map<String, String> filter);

    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    Set<Extension> postFilters(@RestForm MultivaluedMap<String, String> filters);

}

Using @ClientFormParam

Form parameters can also be specified using @ClientFormParam, similar to @ClientQueryParam:

@ClientFormParam(name = "my-param", value = "${my.property-value}")
public interface Client {
    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    String postWithParam();

    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @ClientFormParam(name = "some-other-param", value = "other")
    String postWithOtherParam();

    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @ClientFormParam(name = "param-from-method", value = "{with-param}")
    String postFromMethod();

    default String withParam(String name) {
        if ("param-from-method".equals(name)) {
            return "test";
        }
        throw new IllegalArgumentException();
    }
}

More information about this annotation can be found on the javadoc of @ClientFormParam.

Path Parameters

If the GET request requires path parameters you can leverage the @PathParam("parameter-name") annotation instead of (or in addition to) the @QueryParam. Path and query parameters can be combined, as required, as illustrated in the example below.

package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.QueryParam;
import java.util.Set;

@Path("/extensions")
@RegisterRestClient
public interface ExtensionsService {

    @GET
    @Path("/stream/{stream}")
    Set<Extension> getByStream(@PathParam("stream") String stream, @QueryParam("id") String id);
}

Sending large payloads

The REST Client is capable of sending arbitrarily large HTTP bodies without buffering the contents in memory, if one of the following types is used:

  • InputStream

  • Multi<io.vertx.mutiny.core.buffer.Buffer>

Furthermore, the client can also send arbitrarily large files if one of the following types is used:

  • File

  • Path

Getting other response properties

Using RestResponse

If you need to get more properties of the HTTP response than just the body, such as the status code or headers, you can make your method return org.jboss.resteasy.reactive.RestResponse from a method. An example of this could look like:

package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.jboss.resteasy.reactive.RestQuery;
import org.jboss.resteasy.reactive.RestResponse;

import java.util.Set;

@Path("/extensions")
@RegisterRestClient
public interface ExtensionsService {

    @GET
    RestResponse<Set<Extension>> getByIdResponseProperties(@RestQuery String id);
}
You can also use the Jakarta REST type Response but it is not strongly typed to your entity.

Create the Jakarta REST resource

Create the src/main/java/org/acme/rest/client/ExtensionsResource.java file with the following content:

package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.inject.RestClient;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import java.util.Set;

@Path("/extension")
public class ExtensionsResource {

    @RestClient (1)
    ExtensionsService extensionsService;


    @GET
    @Path("/id/{id}")
    public Set<Extension> id(String id) {
        return extensionsService.getById(id);
    }

    @GET
    @Path("/properties")
    public RestResponse<Set<Extension>> responseProperties(@RestQuery String id) {
        RestResponse<Set<Extension>> clientResponse = extensionsService.getByIdResponseProperties(id); (2)
        String contentType = clientResponse.getHeaderString("Content-Type");
        int status = clientResponse.getStatus();
        String setCookie = clientResponse.getHeaderString("Set-Cookie");
        Date lastModified = clientResponse.getLastModified();

        Log.infof("content-Type: %s status: %s Last-Modified: %s Set-Cookie: %s", contentType, status, lastModified,
                setCookie);

        return RestResponse.fromResponse(clientResponse);
    }
}

There are two interesting parts in this listing:

1 the client stub is injected with the @RestClient annotation instead of the usual CDI @Inject
2 org.jboss.resteasy.reactive.RestResponse used as effective way of getting response properties via RestResponse directly from RestClient, as described in Using RestResponse

创建配置

In order to determine the base URL to which REST calls will be made, the REST Client uses configuration from application.properties. The name of the property needs to follow a certain convention which is best displayed in the following code:

# Your configuration properties
quarkus.rest-client."org.acme.rest.client.ExtensionsService".url=https://stage.code.quarkus.io/api # (1)
1 Having this configuration means that all requests performed using org.acme.rest.client.ExtensionsService will use https://stage.code.quarkus.io/api as the base URL. Using the configuration above, calling the getById method of ExtensionsService with a value of io.quarkus:quarkus-rest-client would result in an HTTP GET request being made to https://stage.code.quarkus.io/api/extensions?id=io.quarkus:quarkus-rest-client.

Note that org.acme.rest.client.ExtensionsService must match the fully qualified name of the ExtensionsService interface we created in the previous section.

To facilitate the configuration, you can use the @RegisterRestClient configKey property that allows to use different configuration root than the fully qualified name of your interface.

@RegisterRestClient(configKey="extensions-api")
public interface ExtensionsService {
    [...]
}
# Your configuration properties
quarkus.rest-client.extensions-api.url=https://stage.code.quarkus.io/api
quarkus.rest-client.extensions-api.scope=jakarta.inject.Singleton

Setting the base URL of the client is mandatory, however the REST Client supports per-invocation overrides of the base URL using the @io.quarkus.rest.client.reactive.Url annotation.

Disabling Hostname Verification

To disable the SSL hostname verification for a specific REST client, add the following property to your configuration:

quarkus.rest-client.extensions-api.verify-host=false

This setting should not be used in production as it will disable the SSL hostname verification.

HTTP/2 Support

HTTP/2 is disabled by default in REST Client. If you want to enable it, you can set:

// for all REST Clients:
quarkus.rest-client.http2=true
// or for a single REST Client:
quarkus.rest-client.extensions-api.http2=true

Alternatively, you can enable the Application-Layer Protocol Negotiation (alpn) TLS extension and the client will negotiate which HTTP version to use over the ones compatible by the server. By default, it will try to use HTTP/2 first and if it’s not enabled, it will use HTTP/1.1. If you want to enable it, you can set:

quarkus.rest-client.alpn=true
// or for a single REST Client:
quarkus.rest-client.extensions-api.alpn=true

Programmatic client creation with QuarkusRestClientBuilder

Instead of annotating the client with @RegisterRestClient, and injecting a client with @RestClient, you can also create REST Client programmatically. You do that with the QuarkusRestClientBuilder.

With this approach the client interface could look as follows:

package org.acme.rest.client;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import java.util.Set;

@Path("/extensions")
public interface ExtensionsService {

    @GET
    Set<Extension> getById(@QueryParam("id") String id);
}

And the service as follows:

package org.acme.rest.client;

import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import java.net.URI;
import java.util.Set;

@Path("/extension")
public class ExtensionsResource {

    private final ExtensionsService extensionsService;

    public ExtensionsResource() {
        extensionsService = QuarkusRestClientBuilder.newBuilder()
            .baseUri(URI.create("https://stage.code.quarkus.io/api"))
            .build(ExtensionsService.class);
    }

    @GET
    @Path("/id/{id}")
    public Set<Extension> id(String id) {
        return extensionsService.getById(id);
    }
}

The QuarkusRestClientBuilder interface is a Quarkus-specific API to programmatically create clients with additional configuration options. Otherwise, you can also use the RestClientBuilder interface from the Microprofile API:

package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.RestClientBuilder;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import java.net.URI;
import java.util.Set;

@Path("/extension")
public class ExtensionsResource {

    private final ExtensionsService extensionsService;

    public ExtensionsResource() {
        extensionsService = RestClientBuilder.newBuilder()
            .baseUri(URI.create("https://stage.code.quarkus.io/api"))
            .build(ExtensionsService.class);
    }

    // ...
}

Use Custom HTTP Options

The REST Client internally uses the Vert.x HTTP Client to make the network connections. The REST Client extensions allows configuring some settings via properties, for example:

  • quarkus.rest-client.client-prefix.connect-timeout to configure the connect timeout in milliseconds.

  • quarkus.rest-client.client-prefix.max-redirects to limit the number of redirects.

However, there are many more options within the Vert.x HTTP Client to configure the connections. See all the options in the Vert.x HTTP Client Options API in this link.

To fully customize the Vert.x HTTP Client instance that the REST Client is internally using, you can provide your custom HTTP Client Options instance via CDI or when programmatically creating your client.

Let’s see an example about how to provide the HTTP Client Options via CDI:

package org.acme.rest.client;

import jakarta.enterprise.inject.Produces;
import jakarta.ws.rs.ext.ContextResolver;

import io.vertx.core.http.HttpClientOptions;
import io.quarkus.arc.Unremovable;

@Provider
public class CustomHttpClientOptions implements ContextResolver<HttpClientOptions> {

    @Override
    public HttpClientOptions getContext(Class<?> aClass) {
        HttpClientOptions options = new HttpClientOptions();
        // ...
        return options;
    }
}

Now, all the REST Clients will be using your custom HTTP Client Options.

Another approach is to provide the custom HTTP Client options when creating the client programmatically:

package org.acme.rest.client;

import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import java.net.URI;
import java.util.Set;

import io.vertx.core.http.HttpClientOptions;

@Path("/extension")
public class ExtensionsResource {

    private final ExtensionsService extensionsService;

    public ExtensionsResource() {
        HttpClientOptions options = new HttpClientOptions();
        // ...

        extensionsService = QuarkusRestClientBuilder.newBuilder()
            .baseUri(URI.create("https://stage.code.quarkus.io/api"))
            .httpClientOptions(options) (1)
            .build(ExtensionsService.class);
    }

    // ...
}
1 the client will use the registered HTTP Client options over the HTTP Client options provided via CDI if any.

Redirection

A HTTP server can redirect a response to another location by sending a response with a status code that starts with "3" and a HTTP header "Location" holding the URL to be redirected to. When the REST Client receives a redirection response from a HTTP server, it won’t automatically perform another request to the new location. We can enable the automatic redirection in REST Client by adding the "follow-redirects" property:

  • quarkus.rest-client.follow-redirects to enable redirection for all REST clients.

  • quarkus.rest-client.<client-prefix>.follow-redirects to enable redirection for a specific REST client.

If this property is true, then REST Client will perform a new request that it receives a redirection response from the HTTP server.

Additionally, we can limit the number of redirections using the property "max-redirects".

One important note is that according to the RFC2616 specs, by default the redirection will only happen for GET or HEAD methods. However, in REST Client, you can provide your custom redirect handler to enable redirection on POST or PUT methods, or to follow a more complex logic, via either using the @ClientRedirectHandler annotation, CDI or programmatically when creating your client.

Let’s see an example about how to register your own custom redirect handler using the @ClientRedirectHandler annotation:

import jakarta.ws.rs.core.Response;

import io.quarkus.rest.client.reactive.ClientRedirectHandler;

@RegisterRestClient(configKey="extensions-api")
public interface ExtensionsService {
    @ClientRedirectHandler
    static URI alwaysRedirect(Response response) {
        if (Response.Status.Family.familyOf(response.getStatus()) == Response.Status.Family.REDIRECTION) {
            return response.getLocation();
        }

        return null;
    }
}

The "alwaysRedirect" redirect handler will only be used by the specified REST Client which in this example is the "ExtensionsService" client.

Alternatively, you can also provide a custom redirect handler for all your REST Clients via CDI:

import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ContextResolver;
import jakarta.ws.rs.ext.Provider;

import org.jboss.resteasy.reactive.client.handlers.RedirectHandler;

@Provider
public class AlwaysRedirectHandler implements ContextResolver<RedirectHandler> {

    @Override
    public RedirectHandler getContext(Class<?> aClass) {
        return response -> {
            if (Response.Status.Family.familyOf(response.getStatus()) == Response.Status.Family.REDIRECTION) {
                return response.getLocation();
            }
            // no redirect
            return null;
        };
    }
}

Now, all the REST Clients will be using your custom redirect handler.

Another approach is to provide it programmatically when creating the client:

@Path("/extension")
public class ExtensionsResource {

    private final ExtensionsService extensionsService;

    public ExtensionsResource() {
        extensionsService = QuarkusRestClientBuilder.newBuilder()
            .baseUri(URI.create("https://stage.code.quarkus.io/api"))
            .register(AlwaysRedirectHandler.class) (1)
            .build(ExtensionsService.class);
    }

    // ...
}
1 the client will use the registered redirect handler over the redirect handler provided via CDI if any.

Update the test

Next, we need to update the functional test to reflect the changes made to the endpoint. Edit the src/test/java/org/acme/rest/client/ExtensionsResourceTest.java file and change the content of the test to:

package org.acme.rest.client;

import io.quarkus.test.junit.QuarkusTest;

import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.greaterThan;

@QuarkusTest
public class ExtensionsResourceTest {

    @Test
    public void testExtensionsIdEndpoint() {
        given()
            .when().get("/extension/id/io.quarkus:quarkus-rest-client")
            .then()
            .statusCode(200)
            .body("$.size()", is(1),
                "[0].id", is("io.quarkus:quarkus-rest-client"),
                "[0].name", is("REST Client"),
                "[0].keywords.size()", greaterThan(1),
                "[0].keywords", hasItem("rest-client"));
    }
}

The code above uses REST Assured's json-path capabilities.

Async Support

To get the full power of the reactive nature of the client, you can use the non-blocking flavor of REST Client extension, which comes with support for CompletionStage and Uni. Let’s see it in action by adding a getByIdAsync method in our ExtensionsService REST interface. The code should look like:

package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import java.util.Set;
import java.util.concurrent.CompletionStage;

@Path("/extensions")
@RegisterRestClient(configKey = "extensions-api")
public interface ExtensionsService {

    @GET
    Set<Extension> getById(@QueryParam("id") String id);

    @GET
    CompletionStage<Set<Extension>> getByIdAsync(@QueryParam("id") String id);
}

Open the src/main/java/org/acme/rest/client/ExtensionsResource.java file and update it with the following content:

package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.inject.RestClient;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import java.util.Set;
import java.util.concurrent.CompletionStage;

@Path("/extension")
public class ExtensionsResource {

    @RestClient
    ExtensionsService extensionsService;


    @GET
    @Path("/id/{id}")
    public Set<Extension> id(String id) {
        return extensionsService.getById(id);
    }

    @GET
    @Path("/id-async/{id}")
    public CompletionStage<Set<Extension>> idAsync(String id) {
        return extensionsService.getByIdAsync(id);
    }
}

Please note that since the invocation is now non-blocking, the idAsync method will be invoked on the event loop, i.e. will not get offloaded to a worker pool thread and thus reducing hardware resource utilization. See Quarkus REST execution model for more details.

To test asynchronous methods, add the test method below in ExtensionsResourceTest:

@Test
public void testExtensionIdAsyncEndpoint() {
    given()
        .when().get("/extension/id-async/io.quarkus:quarkus-rest-client")
        .then()
        .statusCode(200)
        .body("$.size()", is(1),
            "[0].id", is("io.quarkus:quarkus-rest-client"),
            "[0].name", is("REST Client"),
            "[0].keywords.size()", greaterThan(1),
            "[0].keywords", hasItem("rest-client"));
}

The Uni version is very similar:

package org.acme.rest.client;

import io.smallrye.mutiny.Uni;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import java.util.Set;

@Path("/extensions")
@RegisterRestClient(configKey = "extensions-api")
public interface ExtensionsService {

    // ...

    @GET
    Uni<Set<Extension>> getByIdAsUni(@QueryParam("id") String id);
}

The ExtensionsResource becomes:

package org.acme.rest.client;

import io.smallrye.mutiny.Uni;
import org.eclipse.microprofile.rest.client.inject.RestClient;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import java.util.Set;

@Path("/extension")
public class ExtensionsResource {

    @RestClient
    ExtensionsService extensionsService;


    // ...

    @GET
    @Path("/id-uni/{id}")
    public Uni<Set<Extension>> idUni(String id) {
        return extensionsService.getByIdAsUni(id);
    }
}
Mutiny

The previous snippet uses Mutiny reactive types. If you are not familiar with Mutiny, check Mutiny - an intuitive reactive programming library.

When returning a Uni, every subscription invokes the remote service. It means you can re-send the request by re-subscribing on the Uni, or use a retry as follows:

@RestClient ExtensionsService extensionsService;

// ...

extensionsService.getByIdAsUni(id)
    .onFailure().retry().atMost(10);

If you use a CompletionStage, you would need to call the service’s method to retry. This difference comes from the laziness aspect of Mutiny and its subscription protocol. More details about this can be found in the Mutiny documentation.

Server-Sent Event (SSE) support

Consuming SSE events is possible simply by declaring the result type as a io.smallrye.mutiny.Multi.

The simplest example is:

package org.acme.rest.client;

import io.smallrye.mutiny.Multi;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/sse")
@RegisterRestClient(configKey = "some-api")
public interface SseClient {
     @GET
     @Produces(MediaType.SERVER_SENT_EVENTS)
     Multi<String> get();
}

All the IO involved in streaming the SSE results is done in a non-blocking manner.

Results are not limited to strings - for example when the server returns JSON payload for each event, Quarkus automatically deserializes it into the generic type used in the Multi.

Users can also access the entire SSE event by using the org.jboss.resteasy.reactive.client.SseEvent type.

A simple example where the event payloads are Long values is the following:

package org.acme.rest.client;

import io.smallrye.mutiny.Uni;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.reactive.client.SseEvent;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;

@Path("/sse")
@RegisterRestClient(configKey = "some-api")
public interface SseClient {
     @GET
     @Produces(MediaType.SERVER_SENT_EVENTS)
     Multi<SseEvent<Long>> get();
}

Filtering out events

On occasion, the stream of SSE events may contain some events that should not be returned by the client - an example of this is having the server send heartbeat events in order to keep the underlying TCP connection open. The REST Client supports filtering out such events by providing the @org.jboss.resteasy.reactive.client.SseEventFilter.

Here is an example of filtering out heartbeat events:

package org.acme.rest.client;

import io.smallrye.mutiny.Uni;
import java.util.function.Predicate;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.reactive.client.SseEvent;
import org.jboss.resteasy.reactive.client.SseEventFilter;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;

@Path("/sse")
@RegisterRestClient(configKey = "some-api")
public interface SseClient {

     @GET
     @Produces(MediaType.SERVER_SENT_EVENTS)
     @SseEventFilter(HeartbeatFilter.class)
     Multi<SseEvent<Long>> get();


     class HeartbeatFilter implements Predicate<SseEvent<String>> {

        @Override
        public boolean test(SseEvent<String> event) {
            return !"heartbeat".equals(event.id());
        }
     }
}

Custom headers support

There are a few ways in which you can specify custom headers for your REST calls:

  • by registering a ClientHeadersFactory or a ReactiveClientHeadersFactory with the @RegisterClientHeaders annotation

  • by programmatically registering a ClientHeadersFactory or a ReactiveClientHeadersFactory with the QuarkusRestClientBuilder.clientHeadersFactory(factory) method

  • by specifying the value of the header with @ClientHeaderParam

  • by specifying the value of the header by @HeaderParam

The code below demonstrates how to use each of these techniques:

package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam;
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import java.util.Set;
import io.quarkus.rest.client.reactive.NotBody;

@Path("/extensions")
@RegisterRestClient
@RegisterClientHeaders(RequestUUIDHeaderFactory.class) (1)
@ClientHeaderParam(name = "my-header", value = "constant-header-value") (2)
@ClientHeaderParam(name = "computed-header", value = "{org.acme.rest.client.Util.computeHeader}") (3)
public interface ExtensionsService {

    @GET
    @ClientHeaderParam(name = "header-from-properties", value = "${header.value}") (4)
    @ClientHeaderParam(name = "header-from-method-param", value = "Bearer {token}") (5)
    Set<Extension> getById(@QueryParam("id") String id, @HeaderParam("jaxrs-style-header") String headerValue, @NotBody String token); (6)
}
1 There can be only one ClientHeadersFactory per class. With it, you can not only add custom headers, but you can also transform existing ones. See the RequestUUIDHeaderFactory class below for an example of the factory.
2 @ClientHeaderParam can be used on the client interface and on methods. It can specify a constant header value…​
3 …​ and a name of a method that should compute the value of the header. It can either be a static method or a default method in this interface. The method can take either no parameters, a single String parameter or a single io.quarkus.rest.client.reactive.ComputedParamContext parameter (which is very useful for code that needs to compute headers based on method parameters and naturally complements @io.quarkus.rest.client.reactive.NotBody).
4 …​ as well as a value from your application’s configuration
5 …​ or even any mixture of verbatim text, method parameters (referenced by name), a configuration value (as mentioned previously) and method invocations (as mentioned before)
6 …​ or as a normal Jakarta REST @HeaderParam annotated argument

When using Kotlin, if default methods are going to be leveraged, then the Kotlin compiler needs to be configured to use Java’s default interface capabilities. See this for more details.

A ClientHeadersFactory can look as follows:

package org.acme.rest.client;

import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.core.MultivaluedHashMap;
import jakarta.ws.rs.core.MultivaluedMap;
import java.util.UUID;

@ApplicationScoped
public class RequestUUIDHeaderFactory implements ClientHeadersFactory {

    @Override
    public MultivaluedMap<String, String> update(MultivaluedMap<String, String> incomingHeaders, MultivaluedMap<String, String> clientOutgoingHeaders) {
        MultivaluedMap<String, String> result = new MultivaluedHashMap<>();
        result.add("X-request-uuid", UUID.randomUUID().toString());
        return result;
    }
}

As you see in the example above, you can make your ClientHeadersFactory implementation a CDI bean by annotating it with a scope-defining annotation, such as @Singleton, @ApplicationScoped, etc.

To specify a value for ${header.value}, simply put the following in your application.properties:

header.value=value of the header

Also, there is a reactive flavor of ClientHeadersFactory that allows doing blocking operations. For example:

package org.acme.rest.client;

import io.smallrye.mutiny.Uni;

import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.core.MultivaluedHashMap;
import jakarta.ws.rs.core.MultivaluedMap;
import java.util.UUID;

@ApplicationScoped
public class GetTokenReactiveClientHeadersFactory extends ReactiveClientHeadersFactory {

    @Inject
    Service service;

    @Override
    public Uni<MultivaluedMap<String, String>> getHeaders(
            MultivaluedMap<String, String> incomingHeaders,
            MultivaluedMap<String, String> clientOutgoingHeaders) {
        return Uni.createFrom().item(() -> {
            MultivaluedHashMap<String, String> newHeaders = new MultivaluedHashMap<>();
            // perform blocking call
            newHeaders.add(HEADER_NAME, service.getToken());
            return newHeaders;
        });
    }
}

When using HTTP Basic Auth, the @io.quarkus.rest.client.reactive.ClientBasicAuth annotation provides a much simpler way of configuring the necessary Authorization header.

A very simple example is:

@ClientBasicAuth(username = "${service.username}", password = "${service.password}")
public interface SomeClient {

}

where service.username and service.password are configuration properties that must be set at runtime to the username and password that allow access to the service being called.

Default header factory

The @RegisterClientHeaders annotation can also be used without any custom factory specified. In that case the DefaultClientHeadersFactoryImpl factory will be used. If you make a REST client call from a REST resource, this factory will propagate all the headers listed in org.eclipse.microprofile.rest.client.propagateHeaders configuration property from the resource request to the client request. Individual header names are comma-separated.

@Path("/extensions")
@RegisterRestClient
@RegisterClientHeaders
public interface ExtensionsService {

    @GET
    Set<Extension> getById(@QueryParam("id") String id);

    @GET
    CompletionStage<Set<Extension>> getByIdAsync(@QueryParam("id") String id);
}
org.eclipse.microprofile.rest.client.propagateHeaders=Authorization,Proxy-Authorization

Customizing the request

The REST Client supports further customization of the final request to be sent to the server via filters. The filters must implement either the interface ClientRequestFilter or ResteasyReactiveClientRequestFilter.

A simple example of customizing the request would be to add a custom header:

@Provider
public class TestClientRequestFilter implements ClientRequestFilter {

    @Override
    public void filter(ClientRequestContext requestContext) {
        requestContext.getHeaders().add("my_header", "value");
    }
}

Next, you can register your filter using the @RegisterProvider annotation:

@Path("/extensions")
@RegisterProvider(TestClientRequestFilter.class)
public interface ExtensionsService {

    // ...
}

Or programmatically using the .register() method:

QuarkusRestClientBuilder.newBuilder()
    .register(TestClientRequestFilter.class)
    .build(ExtensionsService.class)

Injecting the jakarta.ws.rs.ext.Providers instance in filters

The jakarta.ws.rs.ext.Providers is useful when we need to lookup the provider instances of the current client.

We can get the Providers instance in our filters from the request context as follows:

@Provider
public class TestClientRequestFilter implements ClientRequestFilter {

    @Override
    public void filter(ClientRequestContext requestContext) {
        Providers providers = ((ResteasyReactiveClientRequestContext) requestContext).getProviders();
        // ...
    }
}

Alternatively, you can implement the ResteasyReactiveClientRequestFilter interface instead of the ClientRequestFilter interface that will directly provide the ResteasyReactiveClientRequestContext context:

@Provider
public class TestClientRequestFilter implements ResteasyReactiveClientRequestFilter {

    @Override
    public void filter(ResteasyReactiveClientRequestFilter requestContext) {
        Providers providers = requestContext.getProviders();
        // ...
    }
}

Customizing the ObjectMapper in REST Client Jackson

The REST Client supports adding a custom ObjectMapper to be used only the Client using the annotation @ClientObjectMapper.

A simple example is to provide a custom ObjectMapper to the REST Client Jackson extension by doing:

@Path("/extensions")
@RegisterRestClient
public interface ExtensionsService {

    @GET
    Set<Extension> getById(@QueryParam("id") String id);

    @ClientObjectMapper (1)
    static ObjectMapper objectMapper(ObjectMapper defaultObjectMapper) { (2)
        return defaultObjectMapper.copy() (3)
                .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
                .disable(DeserializationFeature.UNWRAP_ROOT_VALUE);
    }
}
1 The method must be annotated with @ClientObjectMapper.
2 It’s must be a static method. Also, the parameter defaultObjectMapper will be resolved via CDI. If not found, it will throw an exception at runtime.
3 In this example, we’re creating a copy of the default object mapper. You should NEVER modify the default object mapper, but create a copy instead.

Exception handling

The MicroProfile REST Client specification introduces the org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper whose purpose is to convert an HTTP response to an exception.

A simple example of implementing such a ResponseExceptionMapper for the ExtensionsService discussed above, could be:

public class MyResponseExceptionMapper implements ResponseExceptionMapper<RuntimeException> {

    @Override
    public RuntimeException toThrowable(Response response) {
        if (response.getStatus() == 500) {
            throw new RuntimeException("The remote service responded with HTTP 500");
        }
        return null;
    }
}

ResponseExceptionMapper also defines the getPriority method which is used in order to determine the priority with which ResponseExceptionMapper implementations will be called (implementations with a lower value for getPriority will be invoked first). If toThrowable returns an exception, then that exception will be thrown. If null is returned, the next implementation of ResponseExceptionMapper in the chain will be called (if there is any).

The class as written above, would not be automatically be used by any REST Client. To make it available to every REST Client of the application, the class needs to be annotated with @Provider (as long as quarkus.rest-client-reactive.provider-autodiscovery is not set to false). Alternatively, if the exception handling class should only apply to specific REST Client interfaces, you can either annotate the interfaces with @RegisterProvider(MyResponseExceptionMapper.class), or register it using configuration using the providers property of the proper quarkus.rest-client configuration group.

Using @ClientExceptionMapper

A simpler way to convert HTTP response codes of 400 or above is to use the @ClientExceptionMapper annotation.

For the ExtensionsService REST Client interface defined above, an example use of @ClientExceptionMapper would be:

@Path("/extensions")
@RegisterRestClient
public interface ExtensionsService {

    @GET
    Set<Extension> getById(@QueryParam("id") String id);

    @GET
    CompletionStage<Set<Extension>> getByIdAsync(@QueryParam("id") String id);

    @ClientExceptionMapper
    static RuntimeException toException(Response response) {
        if (response.getStatus() == 500) {
            return new RuntimeException("The remote service responded with HTTP 500");
        }
        return null;
    }
}

Naturally this handling is per REST Client. @ClientExceptionMapper uses the default priority if the priority attribute is not set and the normal rules of invoking all handlers in turn apply.

Methods annotated with @ClientExceptionMapper can also take a java.lang.reflect.Method parameter which is useful if the exception mapping code needs to know the REST Client method that was invoked and caused the exception mapping code to engage.

Using @Blocking annotation in exception mappers

In cases that warrant using InputStream as the return type of REST Client method (such as when large amounts of data need to be read):

@Path("/echo")
@RegisterRestClient
public interface EchoClient {

    @GET
    InputStream get();
}

This will work as expected, but if you try to read this InputStream object in a custom exception mapper, you will receive a BlockingNotAllowedException exception. This is because ResponseExceptionMapper classes are run on the Event Loop thread executor by default - which does not allow to perform IO operations.

To make your exception mapper blocking, you can annotate the exception mapper with the @Blocking annotation:

@Provider
@Blocking (1)
public class MyResponseExceptionMapper implements ResponseExceptionMapper<RuntimeException> {

    @Override
    public RuntimeException toThrowable(Response response) {
        if (response.getStatus() == 500) {
            response.readEntity(String.class); (2)
            return new RuntimeException("The remote service responded with HTTP 500");
        }
        return null;
    }
}
1 With the @Blocking annotation, the MyResponseExceptionMapper exception mapper will be executed in the worker thread pool.
2 Reading the entity is now allowed because we’re executing the mapper on the worker thread pool.

Note that you can also use the @Blocking annotation when using @ClientExceptionMapper:

@Path("/echo")
@RegisterRestClient
public interface EchoClient {

    @GET
    InputStream get();

    @ClientExceptionMapper
    @Blocking
    static RuntimeException toException(Response response) {
        if (response.getStatus() == 500) {
            response.readEntity(String.class);
            return new RuntimeException("The remote service responded with HTTP 500");
        }
        return null;
    }
}

Multipart Form support

Sending Multipart messages

REST Client allows sending data as multipart forms. This way you can for example send files efficiently.

To send data as a multipart form, you can just use the regular @RestForm (or @FormParam) annotations:

    @POST
    @Path("/binary")
    String sendMultipart(@RestForm File file, @RestForm String otherField);

Parameters specified as File, Path, byte[], Buffer or FileUpload are sent as files and default to the application/octet-stream MIME type. Other @RestForm parameter types default to the text/plain MIME type. You can override these defaults with the @PartType annotation.

Naturally, you can also group these parameters into a containing class:

    public static class Parameters {
        @RestForm
        File file;

        @RestForm
        String otherField;
    }

    @POST
    @Path("/binary")
    String sendMultipart(Parameters parameters);

Any @RestForm parameter of the type File, Path, byte[], Buffer or FileUpload, as well as any annotated with @PartType automatically imply a @Consumes(MediaType.MULTIPART_FORM_DATA) on the method if there is no @Consumes present.

If there are @RestForm parameters that are not multipart-implying, then @Consumes(MediaType.APPLICATION_FORM_URLENCODED) is implied.

There are a few modes in which the form data can be encoded. By default, REST Client uses RFC1738. You can override it by specifying the mode either on the client level, by setting io.quarkus.rest.client.multipart-post-encoder-mode RestBuilder property to the selected value of HttpPostRequestEncoder.EncoderMode or by specifying quarkus.rest-client.multipart-post-encoder-mode in your application.properties. Please note that the latter works only for clients created with the @RegisterRestClient annotation. All the available modes are described in the Netty documentation

You can also send JSON multiparts by specifying the @PartType annotation:

    public static class Person {
        public String firstName;
        public String lastName;
    }

    @POST
    @Path("/json")
    String sendMultipart(@RestForm @PartType(MediaType.APPLICATION_JSON) Person person);

Programmatically creating the Multipart form

In cases where the multipart content needs to be built up programmatically, the REST Client provides ClientMultipartForm which can be used in the REST Client like so:

public interface MultipartService {

  @POST
  @Path("/multipart")
  @Consumes(MediaType.MULTIPART_FORM_DATA)
  @Produces(MediaType.APPLICATION_JSON)
  Map<String, String> multipart(ClientMultipartForm dataParts);
}

More information about this class and supported methods can be found on the javadoc of ClientMultipartForm.

Converting a received multipart object into a client request

A good example of creating ClientMultipartForm is one where it is created from the server’s MultipartFormDataInput (which represents a multipart request received by Quarkus REST) - the purpose being to propagate the request downstream while allowing for arbitrary modifications:

public ClientMultipartForm buildClientMultipartForm(MultipartFormDataInput inputForm) (1)
    throws IOException {
  ClientMultipartForm multiPartForm = ClientMultipartForm.create(); (2)
  for (Entry<String, Collection<FormValue>> attribute : inputForm.getValues().entrySet()) {
    for (FormValue fv : attribute.getValue()) {
      if (fv.isFileItem()) {
        final FileItem fi = fv.getFileItem();
        String mediaType = Objects.toString(fv.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE),
            MediaType.APPLICATION_OCTET_STREAM);
        if (fi.isInMemory()) {
          multiPartForm.binaryFileUpload(attribute.getKey(), fv.getFileName(),
              Buffer.buffer(IOUtils.toByteArray(fi.getInputStream())), mediaType); (3)
        } else {
          multiPartForm.binaryFileUpload(attribute.getKey(), fv.getFileName(),
              fi.getFile().toString(), mediaType); (4)
        }
      } else {
        multiPartForm.attribute(attribute.getKey(), fv.getValue(), fv.getFileName()); (5)
      }
    }
  }
  return multiPartForm;
}
1 MultipartFormDataInput is a Quarkus REST (Server) type representing a received multipart request.
2 A ClientMultipartForm is created.
3 FileItem attribute is created for the request attribute that represented an in memory file attribute
4 FileItem attribute is created for the request attribute that represented a file attribute saved on the file system
5 Non-file attributes added directly to ClientMultipartForm if not FileItem.

In a similar fashion if the received server multipart request is known and looks something like:

public class Request { (1)

  @RestForm("files")
  @PartType(MediaType.APPLICATION_OCTET_STREAM)
  List<FileUpload> files;

  @RestForm("jsonPayload")
  @PartType(MediaType.TEXT_PLAIN)
  String jsonPayload;
}

the ClientMultipartForm can be created easily as follows:

public ClientMultipartForm buildClientMultipartForm(Request request) { (1)
  ClientMultipartForm multiPartForm = ClientMultipartForm.create();
  multiPartForm.attribute("jsonPayload", request.getJsonPayload(), "jsonPayload"); (2)
  request.getFiles().forEach(fu -> {
    multiPartForm.fileUpload(fu); (3)
  });
  return multiPartForm;
}
1 Request representing the request the server parts accepts
2 A jsonPayload attribute is added directly to ClientMultipartForm
3 A fileUpload is created from the request’s FileUpload

When sending multipart data that uses the same name, problems can arise if the client and server do not use the same multipart encoder mode. By default, the REST Client uses RFC1738, but depending on the situation, clients may need to be configured with HTML5 or RFC3986 mode.

This configuration can be achieved via the quarkus.rest-client.multipart-post-encoder-mode property.

Receiving Multipart Messages

REST Client also supports receiving multipart messages. As with sending, to parse a multipart response, you need to create a class that describes the response data, e.g.

public class FormDto {
    @RestForm (1)
    @PartType(MediaType.APPLICATION_OCTET_STREAM)
    public File file;

    @FormParam("otherField") (2)
    @PartType(MediaType.TEXT_PLAIN)
    public String textProperty;
}
1 uses the shorthand @RestForm annotation to make a field as a part of a multipart form
2 the standard @FormParam can also be used. It allows to override the name of the multipart part.

Then, create an interface method that corresponds to the call and make it return the FormDto:

    @GET
    @Produces(MediaType.MULTIPART_FORM_DATA)
    @Path("/get-file")
    FormDto data receiveMultipart();

At the moment, multipart response support is subject to the following limitations:

  • files sent in multipart responses can only be parsed to File, Path and FileDownload

  • each field of the response type has to be annotated with @PartType - fields without this annotation are ignored

REST Client needs to know the classes used as multipart return types upfront. If you have an interface method that produces multipart/form-data, the return type will be discovered automatically. However, if you intend to use the ClientBuilder API to parse a response as multipart, you need to annotate your DTO class with @MultipartForm.

The files you download are not automatically removed and can take up a lot of disk space. Consider removing the files when you are done working with them.

Multipart mixed / OData usage

It is not uncommon that an application has to interact with enterprise systems (like CRM systems) using a special protocol called OData. This protocol essentially uses a custom HTTP Content-Type which needs some glue code to work with the REST Client (creating the body is entirely up to the application - the REST Client can’t do much to help).

An example looks like the following:

@Path("/crm")
@RegisterRestClient
public interface CRMService {

    @POST
    @ClientHeaderParam(name = "Content-Type", value = "{calculateContentType}")  (1)
    String performBatch(@HeaderParam("Authorization") String accessToken, @NotBody String batchId, String body); (2)

    default String calculateContentType(ComputedParamContext context) {
        return "multipart/mixed;boundary=batch_" + context.methodParameters().get(1).value(); (3)
    }
}

The code uses the following pieces:

1 @ClientHeaderParam(name = "Content-Type", value = "{calculateContentType}") which ensures that the Content-Type header is created by calling the interface’s calculateContentType default method.
2 The aforementioned parameter needs to be annotated with @NotBody because it is only used to aid the construction of HTTP headers.
3 context.methodParameters().get(1).value() which allows the calculateContentType method to obtain the proper method parameter passed to the REST Client method.

As previously mentioned, the body parameter needs to be properly crafted by the application code to conform to the service’s requirements.

Receiving compressed messages

REST Client also supports receiving compressed messages using GZIP. You can enable the HTTP compression support by adding the property quarkus.http.enable-compression=true. When this feature is enabled and a server returns a response that includes the header Content-Encoding: gzip, REST Client will automatically decode the content and proceed with the message handling.

Proxy support

REST Client supports sending requests through a proxy. It honors the JVM settings for it but also allows to specify both:

  • global client proxy settings, with quarkus.rest-client.proxy-address, quarkus.rest-client.proxy-user, quarkus.rest-client.proxy-password, quarkus.rest-client.non-proxy-hosts

  • per-client proxy settings, with quarkus.rest-client.<my-client>.proxy-address, etc. These are applied only to clients injected with CDI, that is the ones created with @RegisterRestClient

If proxy-address is set on the client level, the client uses its specific proxy settings. No proxy settings are propagated from the global configuration or JVM properties.

If proxy-address is not set for the client but is set on the global level, the client uses the global settings. Otherwise, the client uses the JVM settings.

An example configuration for setting proxy:

# global proxy configuration is used for all clients
quarkus.rest-client.proxy-address=localhost:8182
quarkus.rest-client.proxy-user=<proxy user name>
quarkus.rest-client.proxy-password=<proxy password>
quarkus.rest-client.non-proxy-hosts=example.com

# per-client configuration overrides the global settings for a specific client
quarkus.rest-client.my-client.proxy-address=localhost:8183
quarkus.rest-client.my-client.proxy-user=<proxy user name>
quarkus.rest-client.my-client.proxy-password=<proxy password>
quarkus.rest-client.my-client.url=...
MicroProfile REST Client specification does not allow setting proxy credentials. In order to specify proxy user and proxy password programmatically, you need to cast your RestClientBuilder to RestClientBuilderImpl.

Local proxy for dev mode

When using the REST Client in dev mode, Quarkus has the ability to stand up a pass-through proxy which can be used as a target for Wireshark (or similar tools) in order to capture all the traffic originating from the REST Client (this really makes sense when the REST Client is used against HTTPS services)

To enable this feature, all that needs to be done is set the enable-local-proxy configuration option for the configKey corresponding to the client for which proxying is desired. For example:

quarkus.rest-client.my-client.enable-local-proxy=true

When a REST Client does not use a config key (for example when it is created programmatically via QuarkusRestClientBuilder) then the class name can be used instead. For example:

quarkus.rest-client."org.acme.SomeClient".enable-local-proxy=true

The port the proxy is listening can be found in startup logs. An example entry is:

Started HTTP proxy server on http://localhost:38227 for REST Client 'org.acme.SomeClient'

打包并运行该应用程序

使用以下命令运行该应用程序:

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

You should see a JSON object containing some basic information about this extension.

像往常一样,该应用程序可以用以下方式打包:

CLI
quarkus build
Maven
./mvnw install
Gradle
./gradlew build

And executed with java -jar target/quarkus-app/quarkus-run.jar.

你也可以通过以下命令生成本地可执行文件:

CLI
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.native.enabled=true

Logging traffic

REST Client can log the requests it sends and the responses it receives. To enable logging, add the quarkus.rest-client.logging.scope property to your application.properties and set it to:

  • request-response to log the request and response contents, or

  • all to also enable low level logging of the underlying libraries.

As HTTP messages can have large bodies, we limit the amount of body characters logged. The default limit is 100, you can change it by specifying quarkus.rest-client.logging.body-limit.

REST Client is logging the traffic with level DEBUG and does not alter logger properties. You may need to adjust your logger configuration to use this feature.

An example logging configuration:

quarkus.rest-client.logging.scope=request-response
quarkus.rest-client.logging.body-limit=50

quarkus.log.category."org.jboss.resteasy.reactive.client.logging".level=DEBUG

REST Client uses a default ClientLogger implementation, which can be swapped out for a custom implementation.

When setting up the client programmatically using the QuarkusRestClientBuilder, the ClientLogger is set via the clientLogger method.

For declarative clients using @RegisterRestClient, simply providing a CDI bean that implements ClientLogger is enough for that logger to be used by said clients.

Mocking the client for tests

If you use a client injected with the @RestClient annotation, you can easily mock it for tests. You can do it with Mockito’s @InjectMock or with QuarkusMock.

This section shows how to replace your client with a mock. If you would like to get a more in-depth understanding of how mocking works in Quarkus, see the blog post on Mocking CDI beans.

Mocking does not work when using @QuarkusIntegrationTest.

Let’s assume you have the following client:

package io.quarkus.it.rest.client.main;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;


@Path("/")
@RegisterRestClient
public interface Client {
    @GET
    String get();
}

Mocking with InjectMock

The simplest approach to mock a client for tests is to use Mockito and @InjectMock.

First, add the following dependency to your application:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-junit5-mockito</artifactId>
    <scope>test</scope>
</dependency>
build.gradle
testImplementation("io.quarkus:quarkus-junit5-mockito")

Then, in your test you can simply use @InjectMock to create and inject a mock:

package io.quarkus.it.rest.client.main;

import static org.mockito.Mockito.when;

import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
public class InjectMockTest {

    @InjectMock
    @RestClient
    Client mock;

    @BeforeEach
    public void setUp() {
        when(mock.get()).thenReturn("MockAnswer");
    }

    @Test
    void doTest() {
        // ...
    }
}

Mocking with QuarkusMock

If Mockito doesn’t meet your needs, you can create a mock programmatically using QuarkusMock, e.g.:

package io.quarkus.it.rest.client.main;

import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusMock;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
public class QuarkusMockTest {

    @BeforeEach
    public void setUp() {
        Client customMock = new Client() { (1)
            @Override
            public String get() {
                return "MockAnswer";
            }
        };
        QuarkusMock.installMockForType(customMock, Client.class, RestClient.LITERAL); (2)
    }
    @Test
    void doTest() {
        // ...
    }
}
1 here we use a manually created implementation of the client interface to replace the actual Client
2 note that RestClient.LITERAL has to be passed as the last argument of the installMockForType method

Using a Mock HTTP Server for tests

Setting up a mock HTTP server, against which tests are run, is a common testing pattern. Examples of such servers are Wiremock and Hoverfly. In this section we’ll demonstrate how Wiremock can be leveraged for testing the ExtensionsService which was developed above.

First, Wiremock needs to be added as a test dependency. For a Maven project that would happen like so:

pom.xml
<dependency>
    <groupId>org.wiremock</groupId>
    <artifactId>wiremock</artifactId>
    <scope>test</scope>
    <version>${wiremock.version}</version> (1)
</dependency>
1 Use a proper Wiremock version. All available versions can be found here.
build.gradle
testImplementation("org.wiremock:wiremock:$wiremockVersion") (1)
1 Use a proper Wiremock version. All available versions can be found here.

In Quarkus tests when some service needs to be started before the Quarkus tests are ran, we utilize the @io.quarkus.test.common.QuarkusTestResource annotation to specify a io.quarkus.test.common.QuarkusTestResourceLifecycleManager which can start the service and supply configuration values that Quarkus will use.

For more details about @QuarkusTestResource refer to this part of the documentation.

Let’s create an implementation of QuarkusTestResourceLifecycleManager called WiremockExtensions like so:

package org.acme.rest.client;

import java.util.Map;

import com.github.tomakehurst.wiremock.WireMockServer;
import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;

import static com.github.tomakehurst.wiremock.client.WireMock.*; (1)

public class WireMockExtensions implements QuarkusTestResourceLifecycleManager {  (2)

    private WireMockServer wireMockServer;

    @Override
    public Map<String, String> start() {
        wireMockServer = new WireMockServer();
        wireMockServer.start(); (3)

        wireMockServer.stubFor(get(urlEqualTo("/extensions?id=io.quarkus:quarkus-rest-client"))   (4)
                .willReturn(aResponse()
                        .withHeader("Content-Type", "application/json")
                        .withBody(
                            "[{" +
                            "\"id\": \"io.quarkus:quarkus-rest-client\"," +
                            "\"name\": \"REST Client\"" +
                            "}]"
                        )));

        wireMockServer.stubFor(get(urlMatching(".*")).atPriority(10).willReturn(aResponse().proxiedFrom("https://stage.code.quarkus.io/api")));   (5)

        return Map.of("quarkus.rest-client.\"org.acme.rest.client.ExtensionsService\".url", wireMockServer.baseUrl()); (6)
    }

    @Override
    public void stop() {
        if (null != wireMockServer) {
            wireMockServer.stop();  (7)
        }
    }
}
1 Statically importing the methods in the Wiremock package makes it easier to read the test.
2 The start method is invoked by Quarkus before any test is run and returns a Map of configuration properties that apply during the test execution.
3 Launch Wiremock.
4 Configure Wiremock to stub the calls to /extensions?id=io.quarkus:quarkus-rest-client by returning a specific canned response.
5 All HTTP calls that have not been stubbed are handled by calling the real service. This is done for demonstration purposes, as it is not something that would usually happen in a real test.
6 As the start method returns configuration that applies for tests, we set the rest-client property that controls the base URL which is used by the implementation of ExtensionsService to the base URL where Wiremock is listening for incoming requests.
7 When all tests have finished, shutdown Wiremock.

The ExtensionsResourceTest test class needs to be annotated like so:

@QuarkusTest
@QuarkusTestResource(WireMockExtensions.class)
public class ExtensionsResourceTest {

}

@QuarkusTestResource applies to all tests, not just ExtensionsResourceTest.

Known limitations

While the REST Client extension aims to be a drop-in replacement for the RESTEasy Client extension, there are some differences and limitations:

  • the default scope of the client for the new extension is @ApplicationScoped while the quarkus-resteasy-client defaults to @Dependent To change this behavior, set the quarkus.rest-client-reactive.scope property to the fully qualified scope name.

  • it is not possible to set HostnameVerifier or SSLContext

  • a few things that don’t make sense for a non-blocking implementations, such as setting the ExecutorService, don’t work

配置参考

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

类型

默认值

By default, RESTEasy Reactive uses text/plain content type for String values and application/json for everything else.

MicroProfile Rest Client spec requires the implementations to always default to application/json. This build item disables the "smart" behavior of RESTEasy Reactive to comply to the spec

Environment variable: QUARKUS_REST_CLIENT_DISABLE_SMART_PRODUCES

Show more

boolean

false

Whether providers (filters, etc.) annotated with jakarta.ws.rs.ext.Provider should be automatically registered for all the clients in the application.

Environment variable: QUARKUS_REST_CLIENT_PROVIDER_AUTODISCOVERY

Show more

boolean

true

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

类型

默认值

Mode in which the form data are encoded. Possible values are HTML5, RFC1738 and RFC3986. The modes are described in the Netty documentation

By default, Rest Client Reactive uses RFC1738.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT_MULTIPART_POST_ENCODER_MODE

Show more

string

A string value in the form of : that specifies the HTTP proxy server hostname (or IP address) and port for requests of clients to use.

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_PROXY_ADDRESS

Show more

string

Proxy username, equivalent to the http.proxy or https.proxy JVM settings.

Can be overwritten by client-specific settings.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT_PROXY_USER

Show more

string

Proxy password, equivalent to the http.proxyPassword or https.proxyPassword JVM settings.

Can be overwritten by client-specific settings.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT_PROXY_PASSWORD

Show more

string

Hosts to access without proxy, similar to the http.nonProxyHosts or https.nonProxyHosts JVM settings. Please note that unlike the JVM settings, this property is empty by default.

Can be overwritten by client-specific settings.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT_NON_PROXY_HOSTS

Show more

string

A timeout in milliseconds that REST clients should wait to connect to the remote endpoint.

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_CONNECT_TIMEOUT

Show more

long

15000

A timeout in milliseconds that REST clients should wait for a response from the remote endpoint.

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_READ_TIMEOUT

Show more

long

30000

If true, the REST clients will not provide additional contextual information (like REST client class and method names) when exception occurs during a client invocation.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT_DISABLE_CONTEXTUAL_ERROR_MESSAGES

Show more

boolean

false

Default configuration for the HTTP user-agent header to use in all REST clients.

Can be overwritten by client-specific settings.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT_USER_AGENT

Show more

string

The HTTP headers that should be applied to all requests of the rest client.

Environment variable: QUARKUS_REST_CLIENT_HEADERS__HEADER_NAME_

Show more

Map<String,String>

The class name of the host name verifier. The class must have a public no-argument constructor.

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_HOSTNAME_VERIFIER

Show more

string

The time in ms for which a connection remains unused in the connection pool before being evicted and closed. A timeout of 0 means there is no timeout.

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_CONNECTION_TTL

Show more

int

The size of the connection pool for this client.

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_CONNECTION_POOL_SIZE

Show more

int

If set to false disables the keep alive completely.

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_KEEP_ALIVE_ENABLED

Show more

boolean

true

The maximum number of redirection a request can follow.

Can be overwritten by client-specific settings.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT_MAX_REDIRECTS

Show more

int

A boolean value used to determine whether the client should follow HTTP redirect responses.

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_FOLLOW_REDIRECTS

Show more

boolean

Map where keys are fully-qualified provider classnames to include in the client, and values are their integer priorities. The equivalent of the @RegisterProvider annotation.

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_PROVIDERS

Show more

string

The CDI scope to use for injections of REST client instances. Value can be either a fully qualified class name of a CDI scope annotation (such as "jakarta.enterprise.context.ApplicationScoped") or its simple name (such as"ApplicationScoped").

Default scope for the rest-client extension is "Dependent" (which is the spec-compliant behavior).

Default scope for the rest-client-reactive extension is "ApplicationScoped".

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_SCOPE

Show more

string

An enumerated type string value with possible values of "MULTI_PAIRS" (default), "COMMA_SEPARATED", or "ARRAY_PAIRS" that specifies the format in which multiple values for the same query parameter is used.

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_QUERY_PARAM_STYLE

Show more

multi-pairs, comma-separated, array-pairs

Set whether hostname verification is enabled. Default is enabled. This setting should not be disabled in production as it makes the client vulnerable to MITM attacks.

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_VERIFY_HOST

Show more

boolean

The trust store location. Can point to either a classpath resource or a file.

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_TRUST_STORE

Show more

string

The trust store password.

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_TRUST_STORE_PASSWORD

Show more

string

The type of the trust store. Defaults to "JKS".

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_TRUST_STORE_TYPE

Show more

string

The key store location. Can point to either a classpath resource or a file.

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_KEY_STORE

Show more

string

The key store password.

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_KEY_STORE_PASSWORD

Show more

string

The type of the key store. Defaults to "JKS".

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_KEY_STORE_TYPE

Show more

string

The name of the TLS configuration to use.

If not set and the default TLS configuration is configured (quarkus.tls.*) then that will be used. If a name is configured, it uses the configuration from quarkus.tls.<name>.* If a name is configured, but no TLS configuration is found with that name then an error will be thrown.

If no TLS configuration is set, then the keys-tore, trust-store, etc. properties will be used.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT_TLS_CONFIGURATION_NAME

Show more

string

If this is true then HTTP/2 will be enabled.

Environment variable: QUARKUS_REST_CLIENT_HTTP2

Show more

boolean

false

The max HTTP chunk size (8096 bytes by default).

Can be overwritten by client-specific settings.

Environment variable: QUARKUS_REST_CLIENT_MAX_CHUNK_SIZE

Show more

MemorySize 

8k

If the Application-Layer Protocol Negotiation is enabled, the client will negotiate which protocol to use over the protocols exposed by the server. By default, it will try to use HTTP/2 first and if it’s not enabled, it will use HTTP/1.1. When the property http2 is enabled, this flag will be automatically enabled.

Environment variable: QUARKUS_REST_CLIENT_ALPN

Show more

boolean

If true, the stacktrace of the invocation of the REST Client method is captured. This stacktrace will be used if the invocation throws an exception

Environment variable: QUARKUS_REST_CLIENT_CAPTURE_STACKTRACE

Show more

boolean

false

Scope of logging for the client.
WARNING: beware of logging sensitive data
The possible values are:

  • request-response - enables logging request and responses, including redirect responses

  • all - enables logging requests and responses and lower-level logging

  • none - no additional logging This property is applicable to reactive REST clients only.

Environment variable: QUARKUS_REST_CLIENT_LOGGING_SCOPE

Show more

string

How many characters of the body should be logged. Message body can be large and can easily pollute the logs.

By default, set to 100.

This property is applicable to reactive REST clients only.

Environment variable: QUARKUS_REST_CLIENT_LOGGING_BODY_LIMIT

Show more

int

100

The CDI scope to use for injection. This property can contain either a fully qualified class name of a CDI scope annotation (such as "jakarta.enterprise.context.ApplicationScoped") or its simple name (such as "ApplicationScoped"). By default, this is not set which means the interface is not registered as a bean unless it is annotated with RegisterRestClient. If an interface is not annotated with RegisterRestClient and this property is set, then Quarkus will make the interface a bean of the configured scope.

Environment variable: QUARKUS_REST_CLIENT__CLIENTS__SCOPE

Show more

string

If set to true, then Quarkus will ensure that all calls from the REST client go through a local proxy server (that is managed by Quarkus). This can be very useful for capturing network traffic to a service that uses HTTPS.

This property is not applicable to the RESTEasy Client, only the Quarkus REST client (formerly RESTEasy Reactive client).

This property only applicable to dev and test mode.

Environment variable: QUARKUS_REST_CLIENT__CLIENTS__ENABLE_LOCAL_PROXY

Show more

boolean

false

This setting is used to select which proxy provider to use if there are multiple ones. It only applies if enable-local-proxy is true.

The algorithm for picking between multiple provider is the following:

  • If only the default is around, use it (its name is default)

  • If there is only one besides the default, use it

  • If there are multiple ones, fail

Environment variable: QUARKUS_REST_CLIENT__CLIENTS__LOCAL_PROXY_PROVIDER

Show more

string

The base URL to use for this service. This property or the uri property is considered required, unless the baseUri attribute is configured in the @RegisterRestClient annotation.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__URL

Show more

string

The base URI to use for this service. This property or the url property is considered required, unless the baseUri attribute is configured in the @RegisterRestClient annotation.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__URI

Show more

string

This property is only meant to be set by advanced configurations to override whatever value was set for the uri or url. The override is done using the REST Client class name configuration syntax.

This property is not applicable to the RESTEasy Client, only the Quarkus Rest client (formerly RESTEasy Reactive client).

Environment variable: QUARKUS_REST_CLIENT__CLIENT__OVERRIDE_URI

Show more

string

Map where keys are fully-qualified provider classnames to include in the client, and values are their integer priorities. The equivalent of the @RegisterProvider annotation.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__PROVIDERS

Show more

string

Timeout specified in milliseconds to wait to connect to the remote endpoint.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__CONNECT_TIMEOUT

Show more

long

Timeout specified in milliseconds to wait for a response from the remote endpoint.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__READ_TIMEOUT

Show more

long

A boolean value used to determine whether the client should follow HTTP redirect responses.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__FOLLOW_REDIRECTS

Show more

boolean

Mode in which the form data are encoded. Possible values are HTML5, RFC1738 and RFC3986. The modes are described in the Netty documentation

By default, Rest Client Reactive uses RFC1738.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__MULTIPART_POST_ENCODER_MODE

Show more

string

A string value in the form of : that specifies the HTTP proxy server hostname (or IP address) and port for requests of this client to use.

Use none to disable proxy

Environment variable: QUARKUS_REST_CLIENT__CLIENT__PROXY_ADDRESS

Show more

string

Proxy username.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__PROXY_USER

Show more

string

Proxy password.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__PROXY_PASSWORD

Show more

string

Hosts to access without proxy

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__NON_PROXY_HOSTS

Show more

string

An enumerated type string value with possible values of "MULTI_PAIRS" (default), "COMMA_SEPARATED", or "ARRAY_PAIRS" that specifies the format in which multiple values for the same query parameter is used.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__QUERY_PARAM_STYLE

Show more

multi-pairs, comma-separated, array-pairs

Set whether hostname verification is enabled. Default is enabled. This setting should not be disabled in production as it makes the client vulnerable to MITM attacks.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__VERIFY_HOST

Show more

boolean

The trust store location. Can point to either a classpath resource or a file.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__TRUST_STORE

Show more

string

The trust store password.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__TRUST_STORE_PASSWORD

Show more

string

The type of the trust store. Defaults to "JKS".

Environment variable: QUARKUS_REST_CLIENT__CLIENT__TRUST_STORE_TYPE

Show more

string

The key store location. Can point to either a classpath resource or a file.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__KEY_STORE

Show more

string

The key store password.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__KEY_STORE_PASSWORD

Show more

string

The type of the key store. Defaults to "JKS".

Environment variable: QUARKUS_REST_CLIENT__CLIENT__KEY_STORE_TYPE

Show more

string

The class name of the host name verifier. The class must have a public no-argument constructor.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__HOSTNAME_VERIFIER

Show more

string

The name of the TLS configuration to use.

If not set and the default TLS configuration is configured (quarkus.tls.*) then that will be used. If a name is configured, it uses the configuration from quarkus.tls.<name>.* If a name is configured, but no TLS configuration is found with that name then an error will be thrown.

If no TLS configuration is set, then the keys-tore, trust-store, etc. properties will be used.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__TLS_CONFIGURATION_NAME

Show more

string

The time in ms for which a connection remains unused in the connection pool before being evicted and closed. A timeout of 0 means there is no timeout.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__CONNECTION_TTL

Show more

int

The size of the connection pool for this client.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__CONNECTION_POOL_SIZE

Show more

int

If set to false disables the keep alive completely.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__KEEP_ALIVE_ENABLED

Show more

boolean

The maximum number of redirection a request can follow.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__MAX_REDIRECTS

Show more

int

The HTTP headers that should be applied to all requests of the rest client.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__HEADERS__HEADER_NAME_

Show more

Map<String,String>

Set to true to share the HTTP client between REST clients. There can be multiple shared clients distinguished by name, when no specific name is set, the name __vertx.DEFAULT is used.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__SHARED

Show more

boolean

Set the HTTP client name, used when the client is shared, otherwise ignored.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__NAME

Show more

string

Configure the HTTP user-agent header to use.

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__USER_AGENT

Show more

string

If this is true then HTTP/2 will be enabled.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__HTTP2

Show more

boolean

The max HTTP ch unk size (8096 bytes by default).

This property is not applicable to the RESTEasy Client.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__MAX_CHUNK_SIZE

Show more

MemorySize 

8K

If the Application-Layer Protocol Negotiation is enabled, the client will negotiate which protocol to use over the protocols exposed by the server. By default, it will try to use HTTP/2 first and if it’s not enabled, it will use HTTP/1.1. When the property http2 is enabled, this flag will be automatically enabled.

Environment variable: QUARKUS_REST_CLIENT__CLIENT__ALPN

Show more

boolean

If true, the stacktrace of the invocation of the REST Client method is captured. This stacktrace will be used if the invocation throws an exception

Environment variable: QUARKUS_REST_CLIENT__CLIENT__CAPTURE_STACKTRACE

Show more

boolean

About the MemorySize format

A size configuration option recognizes strings in this format (shown as a regular expression): [0-9]+[KkMmGgTtPpEeZzYy]?.

If no suffix is given, assume bytes.

Related content