事情是这样的
注:编写本文时已将敏感代码换为了其他代码代替
本微服务需要调用另一微服务的GET查询接口,且该接口有十来个入参,所以我的FeignClient是这样写的:
@FeignClient(name = "service-b", url = "${service.b.url}")
public interface BFeignClient {
/**
* 查询
*
* @param BRequest ,自定义的Java对象
* @return BResponse ,自定义的Java对象
*/
@RequestMapping(value = "/xxx", method = RequestMethod.GET)
BResponse query(BRequest req);
}
本以为FeignClient会像Spring Boot Controller层一样,自动帮我把BRequest拆解为RequestParam。结果我用抓包工具监听了发出的请求,它转成了POST方式!
(Request-Line):POST /xxx/xxx
Content-Type:application/json;charset=UTF-8
Apitoken:{xxxxxxxxx}
Accept:*/*
User-Agent:Java/1.8.0_162
Host:xxxx:xxx
Connection:keep-alive
Content-Length:154
问题就来了~
找找为什么
当接口入参是自定义的类型时,就会被放在RequestBody里面,以及变成了POST。
GitHub上Spring Cloud也有相关的issue:Suggest supporting POJO as request parameter,另外,
简友周立_itmuch的文章《Spring Cloud中,如何使用Feign构造多参数的请求》提到了这一现象,并且提出了解决办法
两种解决办法
- 将BRequest的各个属性,手动拆开,放到query接口里,类似这样:
//此处@RequestParam必须指明name
@RequestMapping(value = "/xxx", method = RequestMethod.GET)
BResponse query(@RequestParam("a") A a, @RequestParam("b") B b, @RequestParam("c") C c... ...);
但这样麻烦且不雅观
- BRequest 转为 Map<String, Object> map:
//此处@RequestParam不需指明name,具体原因,待考究。。。
@RequestMapping(value = "/xxx", method = RequestMethod.GET)
BResponse query(@RequestParam Map<String, Object> map);
让我选的话,我选第二种方法,哈哈~
Object 转为 Map<String, Object>
麻烦来了,我们的BRequest怎么转为Map<String, Object>呢,百度找到的许多方法都比较麻烦,我希望有现成工具类,终于在StackOverflow上,找到了答案:
How to convert a Java object (bean) to key-value pairs (and vice versa)?
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper m = new ObjectMapper();
//Object to Map<String, Object>
Map<String,Object> props = m.convertValue(myBean, Map.class);
//Map<String, Object> to Object
MyBean anotherBean = m.convertValue(props, MyBean.class);
StackOverflow是个好东西!
关键——ObjectMapper工具类
入门介绍:Jackson ObjectMapper类
它主要是处理Object和Json的互相转换。
完,总结了解决这个问题时的步骤,写得不够深入
网友评论