Feign默认不支持GET方式传递POJO,也不支持传递多个引用类型的参数!本文就是解决这两个问题!
解决思路:
在微服务A端:将要传递的引用类型参数要么以body方式传递,要么以json串方式传递,在拦截器里解析json串拆分为更为详细的参数。
在微服务B端:按照SpringMVC支持写法去写,A端传来的请求参数自动映射到B端实体属性上!
解决问题的关键:是自定义Feign请求拦截器的实现
关键代码如下:
/**
* 处理Feign请求不支持Pojo的问题,以及参数为json串的转换处理!
*/
@AllArgsConstructor
@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {
private ObjectMapper objectMapper;
@Override
public void apply(RequestTemplate template) {
if (template.method().equals("GET")) {
try {
Map<String, Collection<String>> queries = new HashMap<>();
// 处理不带注解的参数解析
// feign 不支持 GET 方法传 POJO, json body转query
if (template.requestBody().asBytes() != null) {
JsonNode jsonNode = objectMapper.readTree(template.requestBody().asBytes());
buildQuery(jsonNode, "", queries);
}
// 处理带有@RequestParam前缀注解的参数解析,如果是json串则拆分处理
Map<String, Collection<String>> entityParams = template.queries();
if (entityParams.size() > 0) {
for (Map.Entry<String, Collection<String>> entry : entityParams.entrySet()) {
List<String> value = (List<String>) entry.getValue();
String strVal = value.get(0);
// 判断是否是引用对象的的参数,例如:%7B%22code%22:%2218041CAB070-018%22%7D
// if (strVal.contains("%22") && strVal.contains("%7B") && strVal.contains("%7D")) { // 则认为是引用类型
JsonNode tmpJsonNode = objectMapper.readTree(URLDecoder.decode(strVal, "UTF-8"));
buildQuery(tmpJsonNode, "", queries);
// }
}
}
template.queries(queries);
} catch (IOException e) {
e.printStackTrace();
}
}
}
...省略其他的代码....
}
解决不能传递POJO的问题:
下面这段代码处理:支持有一个参数不写@RequestParam,这样这个引用参数自动映射为body:即支持get传递POJO对象,例如分页对象,拦截器拦截处理!
Map<String, Collection<String>> queries = new HashMap<>();
// 处理不带注解的参数解析
// feign 不支持 GET 方法传 POJO, json body转query
if (template.requestBody().asBytes() != null) {
JsonNode jsonNode = objectMapper.readTree(template.requestBody().asBytes());
buildQuery(jsonNode, "", queries);
}
例如,下面为feign接口中定义的样例:
@GetMapping("/warninginfo/page")
R getWarningInfoPage(Page page, @RequestParam("warningInfo") String warningInfoJsonString, @RequestHeader(SecurityConstants.FROM) String from);
此处的Page就会被拦截器处理,以便支持传递到B端!
解决传递多个引用对象参数到B端的问题:
下面的这段代码处理带有@RequestParam前缀注解的参数解析,如果是json串则拆分处理:
// 处理带有@RequestParam前缀注解的参数解析,如果是json串则拆分处理
Map<String, Collection<String>> entityParams = template.queries();
if (entityParams.size() > 0) {
for (Map.Entry<String, Collection<String>> entry : entityParams.entrySet()) {
List<String> value = (List<String>) entry.getValue();
String strVal = value.get(0);
// 判断是否是引用对象的的参数,例如:%7B%22code%22:%2218041CAB070-018%22%7D
// if (strVal.contains("%22") && strVal.contains("%7B") && strVal.contains("%7D")) { // 则认为是引用类型
JsonNode tmpJsonNode = objectMapper.readTree(URLDecoder.decode(strVal, "UTF-8"));
buildQuery(tmpJsonNode, "", queries);
// }
}
}
在A端传递多个引用类型对象的json串,这样就支持传递多个引用对象作为参数了,拦截器会解析json串并拆分为请求参数;
下面为feign接口中定义的样例,
@GetMapping("/warninginfo/page")
R getWarningInfoPage(@RequestParam("page") String pageJsonString, @RequestParam("warningInfo") String warningInfoJsonString, @RequestHeader(SecurityConstants.FROM) String from)
B端按照SpringMVC支持的方式书写即可:例如
@GetMapping("/page")
@Inner
public R getWarningInfoPage(Page page, WarningInfo warningInfo) {
return super.getByPage(page, warningInfo);
}
拦截器代码参见有道云笔记地址:http://note.youdao.com/noteshare?id=576a6c4f11b25758a44fb09cb0632151
参考:
https://www.jianshu.com/p/085c11e5722f
https://github.com/spring-cloud/spring-cloud-netflix/issues/1253