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