Java中HTTP GET方法調(diào)用帶有body的問題解決
1.背景描述
上游服務(wù)提供的方法非常比較奇特,查詢接口,定義的GET方法,參數(shù)通過request body傳遞的,在使用Feign Client封裝GET方法調(diào)用時,會遇到一個報錯,“405 Method Not Allowed”。通過查詢,知道這個錯誤原因是HTTP調(diào)用方法錯誤,比如:定義的API是GET方法,通過POST方法(非GET方法)調(diào)用,就會返回這個錯誤。
@RequestLine("GET /api/user/get/") Object getUser(@HeaderMap Map headers, UserRequest request);
2.原因分析
奇怪代碼明明寫得是使用GET方法啊,進一步查資料,得知原因是Feign client框架本身有一個坑:Feign client框架,默認情況下使用的是HttpURLConnection完成實際的http請求調(diào)用,但是HttpURLConnection本身不支持GET方法調(diào)用時帶有body,帶有body的調(diào)用方法,只能是POST方法。
// sun.net.www.protocol.http.HttpURLConnection private synchronized OutputStream getOutputStream0() throws IOException { try { if(!this.doOutput) { throw new ProtocolException("cannot write to a URLConnection if doOutput=false - call setDoOutput(true)"); } else { if(this.method.equals("GET")) { this.method = "POST"; } // ........ } } }
HTTP GET方法調(diào)用,到底支不支持帶有body呢,HTTP協(xié)議是支持的,沒有禁止,但是呢,不建議這么做,不是一個良好的習(xí)慣,因為有些瀏覽器
啥的可能不支持,這個時候,你寫的方法就尷尬了。
Stackoverflow解釋如下:
In other words, any HTTP request message is allowed to contain a message body, and thus must parse messages with that in mind. Server semantics for GET, however,
are restricted such that a body, if any, has no semantic meaning to the request. The requirements on parsing are separate from the requirements on method semantics.
So, yes, you can send a body with GET, and no, it is never useful to do so.
This is part of the layered design of HTTP/1.1 that will become clear again once the spec is partitioned (work in progress).
3.解決方法
方案一
網(wǎng)上可以很容易搜索到這個解決方法,相關(guān)博客非常多,直接copy的情況,太嚴重了。但是實際驗證,沒有生效,具體原因待排查。
1.yml配置文件中,加入feign的配置項:feign.httpclient.enabled: true
2.增加如下maven依賴。
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.3</version> </dependency> <dependency> <groupId>com.netflix.feign</groupId> <artifactId>feign-httpclient</artifactId> <version>8.17.0</version> </dependency>
方案原理:HttpURLConnection不支持GET方法帶有body的調(diào)用,ApacheHttpClient支持GET方法帶有body的調(diào)用。這個配置,就是將feign client默認使用的HTTP調(diào)用方式,從HttpURLConnection切換到ApacheHttpClient方式。
方法不生效原因:
1.可能HTTP調(diào)用方式?jīng)]有切換成功,也就是配置沒有生效。(確定是這個原因,因為我使用的Feign方式:Feign.builder()默認生成的就是HttpURLConnection方式的http請求調(diào)用。相關(guān)源碼如下:
// feign.Feign.Builder private Client client = new Client.Default(null, null); // feign.Client.Default final HttpURLConnection connection = (HttpURLConnection) new URL(request.url()).openConnection();
因此相關(guān)配置修改是不生效的,需要重新生成一個client才行,比如:ApacheHttpClient
2.可能ApacheHttpClient本身也不支持GET方法帶有body的請求。(因為直接使用ApacheHttpClient,發(fā)現(xiàn)沒有支持GET方法帶有body的調(diào)用方式)
補充:(待驗證,證明)
feign分別嘗試了Java原生URLConnection,OkHttp,ApacheHttpClient三種方式:
1.URLConnection 報405錯誤,說明http方法不對,但是feign配置是GET方法,查feign的日志也是用的GET方法。后來發(fā)現(xiàn)原因是URLConnection在的
原因:對于有request body的GET方法,自動改為POST方法了。
2.OkHttp 直接報錯:method GET must not have a request body.
3.ApacheHttpClient完美支持。
方案二
使用AsyncHttpClient,因為AsyncHttpClient支持GET方法帶有Body的調(diào)用。
網(wǎng)上也可以很容易搜索到這個解決方法,感覺都是復(fù)制粘貼的,沒有經(jīng)過驗證和實證,內(nèi)容完全一樣,但是都缺少最關(guān)鍵的信息,沒有給出需要引用的jar包,怎么使用測試呢?而需要引用的jar包還不好找到,實在是大坑。
1.引入maven依賴
<dependency> <groupId>org.asynchttpclient</groupId> <artifactId>async-http-client</artifactId> <version>2.2.0</version> </dependency>
2.解決方法demo
public static String get(String url, String bodyData, Map<String, String> headers) throws Exception { // 構(gòu)建請求 BoundRequestBuilder requestBuilder = asyncHttpClient.prepareGet(url).setBody(bodyData); headers.forEach(requestBuilder::addHeader); List<Response> list = new ArrayList<>(); requestBuilder.execute() .toCompletableFuture() .thenAccept(list::add) .join(); if (list.isEmpty()) { return null; } Response response = list.get(0); if (response.getStatusCode() != 200) { return null; } return response.getResponseBody(); }
備注1:
1.方法可以返回map,增加:new ObjectMapper().readValue(response.getResponseBody(), Map.class);
2.方法本身必須返回json 對象的string才行,不能是非json對象的string,否則解析異常。
備注2:
1.沒有body的get方法,去掉.setBody(bodyData)即可。
2.沒有header的get方法調(diào)用,去掉headers.forEach(requestBuilder::addHeader);即可。
4.總結(jié)
網(wǎng)上資源很多、很豐富,各種問題解決方案很多,但是也存在很多缺陷,不去驗證、實踐,根本不知道里面有問題,因此不要隨便copy別人的博客,往往copy的博客本身就存在潛在的問題,copy之前,請試驗一下,證明方法是正確的,減少給需要同學(xué)的誤導(dǎo)。
到此這篇關(guān)于Java中HTTP GET方法調(diào)用帶有body的問題解決的文章就介紹到這了,更多相關(guān)Java HTTP GET方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Netty分布式pipeline管道傳播事件的邏輯總結(jié)分析
這篇文章主要為大家介紹了Netty分布式pipeline管道傳播事件總結(jié)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-03-03在SpringBoot中實現(xiàn)一個訂單號生成系統(tǒng)的示例代碼
在Spring Boot中設(shè)計一個訂單號生成系統(tǒng),主要考慮到生成的訂單號需要滿足的幾個要求:唯一性、可擴展性、以及可能的業(yè)務(wù)相關(guān)性,本文給大家介紹了幾種常見的解決方案及相應(yīng)的示例代碼,需要的朋友可以參考下2024-02-02Java高性能新一代構(gòu)建工具Maven-mvnd(實踐可行版)
這篇文章主要介紹了Java高性能新一代構(gòu)建工具Maven-mvnd(實踐可行版),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-06-06修改Springboot默認序列化工具Jackson配置的實例代碼
這篇文章主要介紹了如何修改Springboot默認序列化工具Jackson的配置,當Spring容器中存在多個同類型的Bean時,默認情況下最后一個創(chuàng)建的Bean將作為首選Bean,文中通過代碼給大家介紹的非常詳細,需要的朋友可以參考下2024-02-02springBoot靜態(tài)資源加載不到,并且配置了也不生效問題及解決
這篇文章總結(jié)了一個在Spring Boot 2.6.x版本中,由于路徑匹配策略改變導(dǎo)致靜態(tài)資源無法加載的問題,并提供了解決方案:通過配置類或在配置文件中設(shè)置路徑匹配策略為AntPathMatcher,或者直接降級Spring Boot版本2025-02-02