1. 問題
使用的是 2.1.1
版本的 feign
,進過大量的測試,無論是標準是 @PostMapping
還是 @GetMapping
,只要參數標註 @RequestParam
,調用的時候就一律都用 Get
請求,也就是說把參數拼接到 URL
上。如果想使用Post 請求,需要在參數標記 @RequestBody
,這樣無論是 Get
還是 Post
都一律使用 Post
。
以下有幾種的實現方式可以做到配置區分 Post
和 Get
請求。下列方案可能有缺陷,請慎用。
2. 解決辦法
2.1 增加 feign 過濾器
增加的 feign 攔截器,此攔截器是在 RequestTemplate 構建完成後執行的,我們手動再進行加工一下。把Post 相關的參數寫入到 Body 裏面,從而達到目的。
1、增加feign的攔截器 FeignRequestInterceptor
import feign.*;
import feign.template.QueryTemplate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;extHolder;
import org.springframework.web.context.request.ServletRequ
import org.springframework.web.context.request.RequestContestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;
@Slf4j
@Component
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
/處理POST請求的時候轉換成了GET的bug
if (HttpMethod.POST.name().equals(template.method()) && template.requestBody().length() == 0 && !template.queries().isEmpty()) {
Object object = MapUtils.getObject(template.headers(), HttpHeaders.CONTENT_TYPE);
if (null != object) {
if (((Collection) object).contains(APPLICATION_FORM_URLENCODED_VALUE)) {
StringBuilder builder = new StringBuilder();
Map<String, Collection<String>> queries = template.queries();
Iterator<String> queriesIterator = queries.keySet().iterator();
while (queriesIterator.hasNext()) {
String field = queriesIterator.next();
Collection<String> strings = queries.get(field);
//由於參數已經做了url編碼處理,這裏直接拼接即可
builder.append(field + "=" + StringUtils.join(strings, ","));
builder.append("&");
}
template.body(Request.Body.encoded(builder.toString().getBytes(), template.requestCharset()));
template.queries(null);
}
}
}
log.debug("FeignRequestInterceptor:{}", template.toString());
}
}
2、feign 接口定義
// consumes = "application/x-www-form-urlencoded" 是必須設置的,否則不會進入上面寫的處理過程
@PostMapping(value = "/youUrl", consumes = "application/x-www-form-urlencoded")
ResultBody<Map<String,Object>> youMethod(@RequestParam("a") String a, @RequestParam("b") String b);
2.2 使用 httpClient 代替默認實現
使用 httpClient 的實現,雖然 httpClient 裏面的處理是會把URL裏面的參數挪動到Body裏面,但是由於ApacheHttpClient 的實現上和後面的處理有衝突。詳細分析請看另外一篇分析 《Spring boot 使用 feign 調用參數過長(Post變Get)》,但是需要修改 feign 的源碼,這裏直接採用在項目覆蓋默認的實現。從而達到目的。
1、增加 maven
依賴:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.11</version>
</dependency>
<!--<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>10.8</version>
</dependency>-->
2、在yml文件中增加配置
feign:
httpclient:
enabled: true
這個是 FeignAutoConfiguration 自動配置
在根目錄增加 feign.httpclient
package,把 feign-httpclient 中的 ApacheHttpClient 直接複製到此目錄,然後註釋掉此句話即可。
3. 思考
這幾天看了比較多的源碼,feign 相關源碼不應該犯這種錯誤,把 POST 參數放到 URL 上。這個原因我的猜想是由於無論是放到 body 還是 header 上面,其實效果都是一樣的(放到 url和在body編碼方式和組合都是一樣的),header 的默認大小限制就小一點(8K或者更少),而 Post 則大一點(2M+)。換句話來說,其實可以忽略這個問題,直接在配置文件中加大限制即可
server:
port: 4450
# 增加請求頭接受大小
max-http-header-size: 10485760