Search code examples
javaspring-bootspring-webclient

Webclient sending request body with get request


I have a spring-boot project called carrental-crud with an h2 memory database, and I want to access one of the endpoints from another project called carrental-api.

I use webClientBuilder for this on my other endpoints, but it is throwing status 500 with bad request when I try using it in postman.

I use JPArepository to access the h2 db with a query, this is how my orderRepository looks :

@Transactional
@Query(nativeQuery = true,value = "select count(*) from TBL_ORDER WHERE CUSTOMER_ID=?1")
public int getTotalOrders(long customerId);

Then in my adminCustomerService class, I use it like this :

public int getTotalOrders(long customerId) {
    loggerService.writeLoggerMsg("LISTED TOTAL ORDERS FOR A CUSTOMER");
    
    return orderRepository.getTotalOrders(customerId);
}

And then in my adminCustomerController :

@GetMapping(path = "/totalorders")
public int getTotalOrders(@RequestBody Customer customer) {
    return adminCustomerService.getTotalOrders(customer.getCustomerId());
}

and in my postman request body, I write :

{
  "customerId": 2
}

This works in my carrental-crud, but how can I recreate this in my carrental-api?

And using the same request body in postman, but I keep getting the error status 500 with bad request

EDIT : I managed to get this to work by using parameters instead of a request body.

This is how my adminCustomerService looks :

public int getTotalOrders(long customerId) {
   int orders = webClientBuilder.build()
            .get()
            .uri("http://localhost:8081/crud/v1/totalorders/" + customerId)
            .retrieve()
            .bodyToMono(new ParameterizedTypeReference<Integer>() {})
            .log()
            .block();
    return orders;
}

And my adminCustomerController :

@GetMapping(path = "/totalorders/{customerId}")
public int getTotalOrders(@PathVariable long customerId) {

    return adminCustomerService.getTotalOrders(customerId);
}

Solution

  • The issue is that you are simply sending customerId as a Long in your programmatic call when you should be sending something like the JSON that you showed. The simplest way to solve this is by creating a class that matches such JSON structure:

    public class CustomerRequest {
        private long customerId;
    
        public CustomerRequest(long customerId) {
            this.customerId = customerId;
        }
    
        public long getCustomerId() {
            return customerId;
        }
    }
    

    And then you need to create an instance of such class when calling the API as follows:

    public Flux<Integer> getTotalOrders(long customerId) {
        loggerService.writeLoggerMsg("LISTED TOTAL ORDERS FOR A CUSTOMER");
    
        return ((RequestBodySpec) webClientBuilder
                .build()
                .get()
                .uri("localhost:8081/crud/v1/totalorders"))
                .body(Flux.just(new CustomerRequest(customerId)), CustomerRequest.class)
                .retrieve()
                .bodyToFlux(Integer.class);
    }
    

    Still, I would suggest you having a more RESTful approach in your endpoint and use path parameter instead of a request body in a GET request. Something along the following lines:

    @GetMapping(path = "/customers/{customerId}/total-orders")
    public int getTotalOrders(@PathVariable long customerId) {
        return adminCustomerService.getTotalOrders(customerId);
    }
    

    Then you would make a request to this endpoint as follows:

    public Flux<Integer> getTotalOrders(long customerId) {
        loggerService.writeLoggerMsg("LISTED TOTAL ORDERS FOR A CUSTOMER");
    
        return webClientBuilder
                .build()
                .get()
                .uri("localhost:8081/crud/v1/customers/{customerId}/total-orders", customerId))
                .retrieve()
                .bodyToFlux(Integer.class);
    }
    

    I would suggest you read the following online resources: