Skip to content
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

"GET must not have a request body" exception with OkhttpClient and BufferingClientHttpRequestFactory #32612

Closed
shengk opened this issue Apr 10, 2024 · 4 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: bug A general bug
Milestone

Comments

@shengk
Copy link

shengk commented Apr 10, 2024

My project use springboot3.1.8 (spring-web 6.0.16) , and I use RestTemplate with Okhttp4.12.0 for making calls to REST endpoints , the initialize code as follows :

@Bean
public RestTemplate restTemplate() {
        
        RestTemplate restTemplate = new RestTemplate();
        OkHttpClient client = new OkHttpClient().newBuilder().connectionPool(new ConnectionPool(300, 300, TimeUnit.SECONDS)).build();
        OkHttp3ClientHttpRequestFactory httpRequestFactory = new OkHttp3ClientHttpRequestFactory(client);
        restTemplate.setRequestFactory(httpRequestFactory);
        return restTemplate;
 }

and the call code as follows :

@Test
void testGet() {
     String getUrl = "https://api.weixin.qq.com/sns/jscode2session";
     String body = restTemplate.getForEntity(getUrl, String.class).getBody();
}
@Test
void testPost() {
     String postUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=34534543fdgdfg4545";
     Map<String, String> bodyMap = Map.of("code", "xxxxx");
     String body = restTemplate.postForEntity(postUrl, bodyMap, String.class).getBody();
}

the result as follows:

testGet:{"errcode":41002,"errmsg":"appid missing, rid: 661632da-22de497f-36b92d2a"}
testPost:{"errcode":40001,"errmsg":"invalid credential"}

and when I upgrade my springboot version to 3.2.4(spring-web 6.1.5) , the same code run result as follows:


testGet:{"errcode":41002,"errmsg":"appid missing, rid: 661632da-22de497f-36b92d2a"}
testPost:
org.springframework.web.client.HttpClientErrorException: 412 Precondition Failed: [no body]

	at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:136)
	at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:183)
	at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:137)
	at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
	at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:942)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:891)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:790)
	at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:538)
	at com.example.demo.controller.HttpTest.testOkHttpPost(HttpTest.java:28)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

After upgrade , Get request work ok,but post request work error,and then I found the change(spring-web 6.1.X) in wiki

To reduce memory usage in RestClient and RestTemplate, most ClientHttpRequestFactory implementations no longer buffer request bodies before sending them to the server. As a result, for certain content types such as JSON, the contents size is no longer known, and a Content-Length header is no longer set. If you would like to buffer request bodies like before, simply wrap the ClientHttpRequestFactory you are using in a BufferingClientHttpRequestFactory.

so I change My initialize code as follows :

    @Bean
    public RestTemplate restTemplate() {

        RestTemplate restTemplate = new RestTemplate();
        
        OkHttpClient client = new OkHttpClient().newBuilder().connectionPool(new ConnectionPool(300, 300, TimeUnit.SECONDS)).build();
        BufferingClientHttpRequestFactory bufferingClientHttpRequestFactory = new BufferingClientHttpRequestFactory(new OkHttp3ClientHttpRequestFactory(client));
        
        restTemplate.setRequestFactory(bufferingClientHttpRequestFactory);
        return restTemplate;
    }

and I run my code again ,the result as follows:

testGet:
java.lang.IllegalArgumentException: method GET must not have a request body.

	at okhttp3.Request$Builder.method(Request.kt:258)
	at org.springframework.http.client.OkHttp3ClientHttpRequest.executeInternal(OkHttp3ClientHttpRequest.java:88)
	at org.springframework.http.client.AbstractStreamingClientHttpRequest.executeInternal(AbstractStreamingClientHttpRequest.java:70)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66)
	at org.springframework.http.client.BufferingClientHttpRequestWrapper.executeInternal(BufferingClientHttpRequestWrapper.java:75)
	at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:889)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:790)
	at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:431)
	at com.example.demo.controller.HttpTest.testOkHttpGet(HttpTest.java:37)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

testPost:{"errcode":40001,"errmsg":"invalid credential"}

I found , After change(using BufferingClientHttpRequestFactory) , post request work ok , but get request work wrong

so did I miss some configuration? or is there any other suggestions for modifications?

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Apr 10, 2024
@sbrannen sbrannen added the in: web Issues in web modules (web, webmvc, webflux, websocket) label Apr 10, 2024
@bclozel
Copy link
Member

bclozel commented Apr 10, 2024

Do you see the same behavior with okhttp3? While okhttp4 is binary compatible, we don't support this version of the library officially. Also, we have deprecated this request factory entirely, see #30919.

Can you reproduce the same behavior with a public API like httpbin.org? It would be easier for us to investigate the issue if we can reproduce the problem using a sample app that you could provide.

@bclozel bclozel added the status: waiting-for-feedback We need additional information before we can continue label Apr 10, 2024
@shengk
Copy link
Author

shengk commented Apr 10, 2024

I haven't tested with okhttp3 or public API yet , and I can give it a try。

and I can provide another Information : the same API like I described above, I have tested with httpclient5,and Get、Post request work ok with BufferingClientHttpRequestFactory

    @Bean
    public RestTemplate httpComponentRestTemplate() {

        RestTemplate restTemplate = new RestTemplate();
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClientBuilder().build());

        BufferingClientHttpRequestFactory bufferingClientHttpRequestFactory = new BufferingClientHttpRequestFactory(clientHttpRequestFactory);


        restTemplate.setRequestFactory(bufferingClientHttpRequestFactory);
        return restTemplate;
    }

    private HttpClientBuilder httpClientBuilder() {
        return HttpClients.custom().setConnectionManager(poolingHttpClientConnectionManager());
    }

    private PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(1000);
        connectionManager.setDefaultMaxPerRoute(500);
        return connectionManager;
    }

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Apr 10, 2024
@bclozel bclozel added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Apr 10, 2024
@shengk
Copy link
Author

shengk commented Apr 10, 2024

I have finish test,with public API:https://httpbin.org/get ,still getting an error

@Test
void testGet() {
        String getUrl = "https://httpbin.org/get";
        String body = restTemplate.getForEntity(getUrl, String.class).getBody();
        System.out.println(body);
 }
java.lang.IllegalArgumentException: method GET must not have a request body.

	at okhttp3.Request$Builder.method(Request.kt:258)
	at org.springframework.http.client.OkHttp3ClientHttpRequest.executeInternal(OkHttp3ClientHttpRequest.java:88)
	at org.springframework.http.client.AbstractStreamingClientHttpRequest.executeInternal(AbstractStreamingClientHttpRequest.java:70)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66)
	at org.springframework.http.client.BufferingClientHttpRequestWrapper.executeInternal(BufferingClientHttpRequestWrapper.java:75)
	at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:889)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:790)
	at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:431)
	at com.example.demo.test.HttpTest.testGet(HttpTest.java:37)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

and All scenes i use okHttp3.14.9,It's the same situation

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Apr 10, 2024
@bclozel bclozel self-assigned this Apr 10, 2024
@bclozel bclozel added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged or decided on status: feedback-provided Feedback has been provided labels Apr 10, 2024
@bclozel bclozel changed the title RestTemplate with OkhttpClient use Get request error in spring-web 6.1.5 by using BufferingClientHttpRequestFactory "GET must not have a request body" exception with OkhttpClient and BufferingClientHttpRequestFactory Apr 10, 2024
@shengk
Copy link
Author

shengk commented Apr 10, 2024

I Trace the source code,in spring-web(6.1.5) the error throws at spring's OkHttp3ClientHttpRequest ->executeInternal() ->builder.method(this.method.name(), requestBody)

and At this time,requestBody is not null,it has body and headers two nodes,and body's running type is BufferingClientHttpRequestWrapper . body has two nodes too , the first is bufferedOutput ,and it’s value is not null but an Empty array,I don't know if it's a problem here

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) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

5 participants