Request Customization

About

When interacting with external services, it is often necessary to customize HTTP requests adding headers, cookies, setting timeouts, modifying the request body, or even intercepting requests conditionally. WebClient offers a rich and fluent API for such customizations.

HTTP Methods

WebClient supports all standard HTTP methods

HTTP Method
WebClient Usage Example
Description

GET

webClient.get()

Retrieve data without modifying server state

POST

webClient.post()

Send data to create resources

PUT

webClient.put()

Send full updates to existing resources

PATCH

webClient.patch()

Send partial updates

DELETE

webClient.delete()

Remove a resource

HEAD

webClient.method(HttpMethod.HEAD)

Retrieve only headers

OPTIONS

webClient.method(HttpMethod.OPTIONS)

Discover supported HTTP methods

TRACE

webClient.method(HttpMethod.TRACE)

Debugging or tracing path of a request

All methods internally use below when more flexibility is needed.

webClient.method(HttpMethod.X)

Setting Multiple Query Parameters

Use uriBuilder.queryParam(...) repeatedly to add multiple query parameters.

Mono<Response> response = webClient.get()
    .uri(uriBuilder -> uriBuilder
        .path("/orders")
        .queryParam("status", "PENDING")
        .queryParam("sortBy", "date")
        .queryParam("page", 1)
        .queryParam("limit", 20)
        .build())
    .retrieve()
    .bodyToMono(Response.class);

If we are using dynamic values, use a map:

Map<String, Object> queryParams = Map.of(
    "status", "PENDING",
    "sortBy", "date",
    "page", 1,
    "limit", 20
);

Mono<Response> response = webClient.get()
    .uri(uriBuilder -> {
        UriBuilder builder = uriBuilder.path("/orders");
        queryParams.forEach(builder::queryParam);
        return builder.build();
    })
    .retrieve()
    .bodyToMono(Response.class);

Setting Multiple Path Variables

Use .uri(String template, Object... uriVariables) with multiple values:

Mono<ProductDetail> response = webClient.get()
    .uri("/stores/{storeId}/categories/{categoryId}/products/{productId}", 
         storeId, categoryId, productId)
    .retrieve()
    .bodyToMono(ProductDetail.class);

Using a Map<String, ?>:

Map<String, String> pathVars = Map.of(
    "storeId", "101",
    "categoryId", "electronics",
    "productId", "555"
);

Mono<ProductDetail> response = webClient.get()
    .uri(uriBuilder -> uriBuilder
        .path("/stores/{storeId}/categories/{categoryId}/products/{productId}")
        .build(pathVars))
    .retrieve()
    .bodyToMono(ProductDetail.class);

Setting Request Body

Request body is usually needed for POST, PUT, PATCH.

a. Single Java Object

UserRequest request = new UserRequest("[email protected]", "John", "Doe");

Mono<UserResponse> response = webClient.post()
    .uri("/users")
    .bodyValue(request)
    .retrieve()
    .bodyToMono(UserResponse.class);

b. List or Collection

List<OrderRequest> orders = List.of(
    new OrderRequest("item-1", 2),
    new OrderRequest("item-2", 1)
);

Flux<OrderRequest> requestFlux = Flux.fromIterable(orders);

Flux<OrderResponse> response = webClient.post()
    .uri("/bulk-orders")
    .body(BodyInserters.fromPublisher(requestFlux, OrderRequest.class))
    .retrieve()
    .bodyToFlux(OrderResponse.class);

Setting Headers

Headers can be set using the headers method inside the request spec.

Mono<CustomerResponse> response = webClient.get()
    .uri("/customers/{id}", customerId)
    .headers(httpHeaders -> {
        httpHeaders.set("X-Correlation-ID", UUID.randomUUID().toString());
        httpHeaders.setBearerAuth(jwtToken);
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        httpHeaders.setAccept(List.of(MediaType.APPLICATION_JSON));
    })
    .retrieve()
    .bodyToMono(CustomerResponse.class);

We can also use header(String, String...) directly for quick one-liners:

webClient.get()
    .uri("/orders")
    .header("X-Client-ID", "my-app")
    .header(HttpHeaders.AUTHORIZATION, "Bearer " + jwtToken)
    .retrieve()
    .bodyToMono(OrderSummary.class);

Adding Cookies

Use .cookies() to add one or more cookies to the request.

Mono<LoginStatus> response = webClient.get()
    .uri("/session/check")
    .cookies(cookies -> {
        cookies.add("SESSIONID", sessionId);
        cookies.add("clientType", "web");
    })
    .retrieve()
    .bodyToMono(LoginStatus.class);

Change content type / accept type

These are crucial for telling the server what format we are sending and what we expect in return.

a. Set Content-Type (what we are sending)

webClient.post()
    .uri("/users")
    .contentType(MediaType.APPLICATION_JSON)
    .bodyValue(new UserRequest("[email protected]", "John"))
    .retrieve()
    .bodyToMono(UserResponse.class);

Other content types:

  • MediaType.APPLICATION_XML

  • MediaType.MULTIPART_FORM_DATA

  • MediaType.APPLICATION_FORM_URLENCODED

b. Set Accept Header (what we want back)

webClient.get()
    .uri("/catalog")
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .bodyToMono(CatalogResponse.class);

We can specify multiple types if needed:

webClient.get()
    .uri("/report")
    .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
    .retrieve()
    .bodyToMono(Report.class);

Last updated