FeignClient调用报错405请求方法错误

文章目录

    • 背景
    • 原因
    • FeignClient调用流程
    • 简化后主要流程

背景

  • FeignClient和GetMapping组合报错405请求方法错误
    在编写代码的时候,遇到一个情况,在使用FeignClient调用服务,因为是获得数据的接口,并且参数有很多,所以封装成一个DTO,但是却总是报错,报的还是很奇怪的405,请求方法错误,通过全局异常捕捉时,发现在服务端显示的是调用源POST调用,可是我明明使用FeignClient的GetMapping发起的。
    (因为想着在SpringMVC可以直接接收实体对象,并且@RequestParam并不能直接作用于一个实体对象上,所以就直接如下写了FeignClient)
@FeignClient("xdz-account-service")
public interface XdzAccountClient{
    @GetMapping("/xxx/xxx/xxx")
    Result getCmsMerchant(CmsMerchantDTO dto);
}

原因

事实上,通过分析造成如此的原因,我也整理了一下FeignClient的调用流程。这里先给出结论,为什么FeignClient发起的GetMapping会报405,是因为FeignClient最后是用HttpURLConnectiion发起的网络连接,在发起的过程中,Connection会判断其自身的body是否为空,如果不为空,则将 GET Method 转换为 POST Method。

这也容易理解,因为body数据只能放在RquestBody内以流的形式传输,而param数据则在Http协议中直接放在URL上进行传输和获取,所以如果有body数据,理应转为POST请求,这也每种数据都能正确的传输到网络接口。
FeignClient调用报错405请求方法错误_第1张图片
但是,我们的服务端生产者是Get请求的接口,这样直接就会导致请求类型不一致报405,而为什么会造成转换,是因为body数据不为空,但是为什么body会有值,这要从初始化FeignClient和FeignClient的规则说起。
在启动过程中,就将FeignClient注解的类进行动态代理,且初始化了代理类。

  1. 如果有@RequestParam(“xx”)注解,则会将参数作为key,放入RequestTemplate.Factory中,由urlIndex来指明数组索引,并且在解析的时候,根据每个key从arg数组获取到具体值,拼接在url后面;
  2. 如果没有任何注解,或者用@RequestBody贴在参数前面,则初始化RequestTemplate.Factory的时候,bodyIndex会维护参数索引,并且bodyType这个参数会携带参数类型

所以我们现在的场景是没有加任何注解,FeignClient会把数据放入body中,那么怎么解决呢,可以直接加@RequestParam注解吗,如改写成下面这样?

@FeignClient("xdz-account-service")
public interface XdzAccountClient{
    @GetMapping("/xxx/xxx/xxx")
    Result getCmsMerchant(@RequestParam("dto") CmsMerchantDTO dto);
}

答案是否定的,因为@RequestParam只能注解单个基本数据类型,上面的写法将会让url的dto的值是dto.toString()。
我们可以将dto的数据全部拿出来,一个一个写在参数列表上,并都贴上@RequestParam注解,但是这样写法无异于让参数列表变的很长。

为此,如果我们要传递这种封装了多个数据的实体数据,又不想一个一个拿出来写在参数列表,我们在FeignClient中就要打破RESTful规范,使用POST来发起请求调用,同时服务端也使用POST来接收。
(按照上面的GET会转POST的理论,如果我们FeignClient调用端写的是GetMapping,参数不贴注解,只要服务端的生产者是PSOT请求加@RequestBody接收,那么也能正确接收并响应数据,经过实验发现确实如此)

下面是笔者在追踪源码的过程中整理的FeignClient调用流程

FeignClient调用流程

FeignClient调用报错405请求方法错误_第2张图片

简化后主要流程

FeignClient调用报错405请求方法错误_第3张图片

你可能感兴趣的:(源码分析,SpringCloud,分布式,java)