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
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!