• 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
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    OS + Multipass
    服务器间文件实时双向同步(rsync+inotify)
    全链路追踪 & 性能监控工具 SkyWalking 实战
    TCP Dup ACK linux kernel 3.2
    Ns3.35 errata Compilation on Python 3.10 systems
    LeetCode 108. Convert Sorted Array to Binary Search Tree
    LeetCode 98. Validate Binary Search Tree
    LeetCode 701. Insert into a Binary Search Tree
    LeetCode 235. Lowest Common Ancestor of a Binary Search Tree
    LeetCode 783. Minimum Distance Between BST Nodes
  • 原文地址:https://www.cnblogs.com/softidea/p/11088460.html
Copyright © 2020-2023  润新知