Skip to content

RestClient Bean can not call API with method GET contains body #32948

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ngocnhan-tran1996 opened this issue Jun 4, 2024 · 2 comments
Closed
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: invalid An issue that we don't feel is valid

Comments

@ngocnhan-tran1996
Copy link
Contributor

Context

I have to call an API have body with method GET and I use RestClient

Issue

I create bean RestClient by this way

@Bean
RestClient builderRestClient(RestClient.Builder builder) {

  return builder.build();
}

The way I call API

restClient.method(HttpMethod.GET)
          .uri(URI.create("http://localhost:8080/get"))
          .headers(httpHeaders -> httpHeaders.setContentType(MediaType.APPLICATION_JSON))
          .body(body)
          .retrieve()
          .body(Map.class);

When I call API, the reponse like

org.springframework.web.client.HttpClientErrorException$BadRequest: 400 : "{"timestamp":"2024-06-03T23:48:44.044+00:00","status":400,"error":"Bad Request","path":"/get"}"
	at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:103) ~[spring-web-6.1.8.jar:6.1.8]
	at org.springframework.web.client.StatusHandler.lambda$defaultHandler$3(StatusHandler.java:86) ~[spring-web-6.1.8.jar:6.1.8]
	at org.springframework.web.client.StatusHandler.handle(StatusHandler.java:146) ~[spring-web-6.1.8.jar:6.1.8]

But if I create bean by this way, I can call API

@Bean
RestClient builderRestClient() {

    return RestClient.create();
}

How to reproduce

I have a demo in this repo. Please take a look and tell me if I miss something.
https://github.com/ngocnhan-tran1996/restclient-demo

Spring version: 3.3.0
Java version: 22

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jun 4, 2024
@bclozel bclozel self-assigned this Jun 4, 2024
@snicoll
Copy link
Member

snicoll commented Jun 4, 2024

Thanks for the sample. The underlying ClientHttpRequestFactory are not the same. When you create the client yourself, you get Spring Framework's default, i.e. org.springframework.http.client.JdkClientHttpRequestFactory. If you configure the client using the builder that Spring Boot has auto-configured for you, you get a org.springframework.http.client.SimpleClientHttpRequestFactory.

The former is new in 6.1 and Spring Boot couldn't swap the default behavior for backward compatible reason, see spring-projects/spring-boot#38856 and the issue that it links.

I am not sure why SimpleClientHttpRequestFactory wouldn't write the body. @poutsma, do you know?

@snicoll snicoll added status: waiting-for-feedback We need additional information before we can continue labels Jun 4, 2024
@bclozel bclozel removed their assignment Jun 4, 2024
@snicoll snicoll added status: waiting-for-internal-feedback An issue that needs input from a member or another Spring Team and removed status: waiting-for-feedback We need additional information before we can continue labels Jun 4, 2024
@bclozel
Copy link
Member

bclozel commented Jun 4, 2024

I've tracked this behavior to #10207. This is because HttpUrlConnection does not support setting a request body for "GET" requests and will turn it into a POST request automatically. In Spring, our request factory will avoid sending the body altogether to avoid turning it into a POST.

See the JDK code in action here: https://github.com/openjdk/jdk/blob/jdk-23%2B25/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java#L1431-L1433

You can verify that in action without Spring being involved with:

String body = "test body";
URL url = new URL("http://localhost:"+port+"/test");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setFixedLengthStreamingMode(body.length());
con.setDoOutput(true);
con.setRequestProperty("Content-Type", MediaType.APPLICATION_JSON_VALUE);
OutputStream outputStream = con.getOutputStream();
outputStream.write(body.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();

int responseCode = con.getResponseCode();
assertThat(responseCode).isEqualTo(200);

This will send the request as a POST:

2024-06-04T12:07:37.206+02:00 DEBUG 11214 --- [restclient] [    Test worker] s.n.www.protocol.http.HttpURLConnection  : sun.net.www.MessageHeader@249b54af7 pairs: {POST /test HTTP/1.1: null}{Content-Type: application/json}{User-Agent: Java/17.0.11}{Host: localhost:52192}{Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2}{Connection: keep-alive}{Content-Length: 9}

In this case @ngocnhan-tran1996 , please use a different request factory for your client. You can configure the request factory manually for your entire application or add a library on the classpath that will be picked up instead of the default one.

@bclozel bclozel closed this as not planned Won't fix, can't repro, duplicate, stale Jun 4, 2024
@bclozel bclozel added in: web Issues in web modules (web, webmvc, webflux, websocket) status: invalid An issue that we don't feel is valid and removed status: waiting-for-triage An issue we've not yet triaged or decided on status: waiting-for-internal-feedback An issue that needs input from a member or another Spring Team labels Jun 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

4 participants