Feign發送Get請求時,采用POJO對象傳遞參數的最終解決方案 Request method 'POST' not supported (附帶其余好幾個坑)


yml:

feign:
  httpclient:
     enabled: true

properties:

#feign
feign.httpclient.enabled=true

 

        <!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-httpclient -->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
            <version>10.2.3</version>
        </dependency>

 

 https://blog.csdn.net/u010277958/article/details/88730889

 

相關閱讀
【小家java】java5新特性(簡述十大新特性) 重要一躍
【小家java】java6新特性(簡述十大新特性) 雞肋升級
【小家java】java7新特性(簡述八大新特性) 不溫不火
【小家java】java8新特性(簡述十大新特性) 飽受贊譽
【小家java】java9新特性(簡述十大新特性) 褒貶不一
【小家java】java10新特性(簡述十大新特性) 小步迭代
【小家java】java11新特性(簡述八大新特性) 首個重磅LTS版本

每篇一句
如果你真的覺得很難,那你就放棄。但是你放棄了你就不要抱怨說,我為什么沒有得到

前言
spring cloud技術棧里面,Feign可以使得我們的rest調用和調用本地方法一樣方便。但是它真的有非常多的坑,苦不堪言啊。本文將描述我們最為常遇到的坑:

Feign發送Get請求時,采用POJO傳遞參數 Request method ‘POST’ not supported

坑 例舉
Feign發送Get請求時,采用POJO傳遞參數的坑
在使用Feign client來調用Get請求接口時,如果方法的參數是一個對象,例如:

@FeignClient("microservice-provider-user")
public interface UserFeignClient {

@RequestMapping(value = "/user", method = RequestMethod.GET)
public PageBean<User> get(User user);

}


我們想得好好的。分頁查詢,查詢條件用POJO的User對象進行包裝進去。但奈何:在調試的時候你會一臉懵逼,因為報了如下錯誤:

feign.FeignException: status 405 reading UserFeignClient#get0(User); content:
{"timestamp":1482676142940,"status":405,"error":"Method Not Allowed", "exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'POST' not supported","path":"/user"}

what?老夫明明用的get請求啊,你竟然說Post方法不支持?

其實這個問題,在feign的github社區里面,一直有人提出了issue,只是一直沒有被解決而已。

github上相關issue參考:

希望Feign能夠支持參數請求使用POJO:https://github.com/spring-cloud/spring-cloud-netflix/issues/1253
解決辦法:http://www.itmuch.com/spring-cloud-sum/feign-multiple-params/
建議使用Feign原生的注解的Issue:https://github.com/spring-cloud/spring-cloud-netflix/issues/659
建議增強Feign的功能:https://github.com/spring-cloud/spring-cloud-netflix/issues/1360
建議支持可選的Request Body(目前Feign當POST一個null時,會報異常):https://github.com/spring-cloud/spring-cloud-netflix/issues/1047
雖然可以采用@RequestParam的方式解決問題,但是很惱火的我,仔細想想:

你想寫一堆長長的參數嗎?用一個不知道里邊有什么鬼的Map嗎?或者轉換為post?這似乎與REST風格不太搭,會浪費url資源,我們還需要在url定義上來區分Get或者Post。

於是就開始逐行調試,知道我從feign的源碼中發現了這個:

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";
}

這段代碼是在 HttpURLConnection 中發現的,jdk原生的http連接請求工具類,原來是因為Feign默認使用的連接工具實現類,所以里面發現只要你有body體對象,就會強制的把get請求轉換成POST請求。

終上所述,這也不能怪feign,是HttpURLConnection 的問題。所以接下來我准備換一個HttpClient試試,因此本利我采用apache的HttpClient。但是一定,一定需要加入如下幾個步驟:

加入feign的配置項:feign.httpclient,enabled = true
在依賴中引入apache的httpclient

<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>

 


配置上此依賴(此依賴不可少 否則不生效的)
<!-- 使用Apache HttpClient替換Feign原生httpclient -->

<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>${feign-httpclient}</version>
</dependency>



按照上面3個步驟添加好依賴后,我們可以很自由的使用User對象來傳遞get請求的參數了,是不是很優雅有木有。

但是一波三折,我發現服務端接受到的值都是null。因此我只能這么搞了

@FeignClient("microservice-provider-user")
public interface UserFeignClient {

@RequestMapping(value = "/user", method = RequestMethod.GET)
public PageBean<User> get(@RequestBody User user);

}

 

 


竟然在get請求里加上這么一個注解。結果,好使了。哈哈,完美

Feign 傳參問題及傳輸Date類型參數的時差 坑
feign的調用如下:

List<LeftSeatCountOfDaysResp> getLeftSeatCountOfDays(
@RequestParam("configType") Integer configType,
@RequestParam("courseId") Long courseId,
@RequestParam("startDateFrom") Date startDateFrom,
@RequestParam("startDateTo") Date startDateTo,
@RequestParam("level") Integer level);

 



我們采用了兩個date類型的參數傳參,結果:
我們傳入的時間為:

但服務端接受到的時間為:

天啊擼,竟然出現了我們並不熟悉的14h時差,並不是我們熟悉的8個小時。feign真是天坑啊。這是SpringCloud Feign傳Date類型參數的時差導致的。

備注:使用date類型傳參,如果是body里面用對象傳,是不會出現時差問題的。

下面說說兩種解決方案:

當發送時間類型時,直接用String發送(推薦)
Feign客戶端實現FeignFormatterRegistrar接口自定義DateFormatRegister

@Component
public class DateFormatRegister implements FeignFormatterRegistrar{

public DateFormatRegister(){
}

@Override
public void registerFormatters(FormatterRegistry registry) {
registry.addConverter(Date.class, String.class, new Date2StringConverter()); 
}

private class Date2StringConverter implements Converter<Date,String>{

@Override
public String convert(Date source) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(source);
}

}
}

 

服務端實現:

@Configuration
public class WebConfigBeans {
@Autowired
private RequestMappingHandlerAdapter handlerAdapter;

/**
* 增加字符串轉日期的功能
*/
@PostConstruct
public void initEditableValidation() {
ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) handlerAdapter
.getWebBindingInitializer();
if (initializer.getConversionService() != null) {
GenericConversionService genericConversionService = (GenericConversionService) initializer
.getConversionService();
genericConversionService.addConverter(String.class, Date.class, new String2DateConverter());
}
}
}

 

 


第二種比較麻煩,但是一勞永逸,代碼的優雅性比第一種好。但個人而言,還是推薦使用第一種。

Feign 傳參時候使用@DateTimeFormat注解 坑

@NotNull
@MyFuture
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date appointDate; //預定的預成班日期

 

比如這個字段,服務端上面用了@DateTimeFormat注解,這樣的話,springMVC手機支持直接傳字符串2018-03-03自動轉換的。但是,但是,如果你是用client調用,那就不報錯啦,報錯啦。所以使用的時候,一定要注意啊,一定要注意啊。

總結:
雖然fiegn有很多坑,但咱不能說feign不好用。畢竟他比restTemplate或者httpClient還是優雅很多的,能夠簡化很多東西,負載均衡也做得不錯,畢竟在本地就可以做。
---------------------
作者:_YourBatman
來源:CSDN
原文:https://blog.csdn.net/f641385712/article/details/82431502
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM