作者:Grey
原文地址:
Spring MVC 支援 GET 方法直接繫結 POJO 的,但是 Feign 目前無法做到,有幾種解決方案
方案一:把 POJO 拆散成一個一個單獨的屬性放在方法引數裡。
方案二:把方法引數變成Map傳遞。
方案三:使用 GET 傳遞 @RequestBody ,但此方式違反 Restful 規範。
方案四(最佳實踐):通過實現 Feign 的 RequestInterceptor 中的 apply 方法來進行統一攔截轉換處理 Feign 中的 GET 方法多引數傳遞的問題。
接下來介紹方案四,即最佳實踐。
Java 版本:17
Spring Boot 版本:2.7.5
Spring Cloud 版本:2021.0.5
provider 專案中,定義了一個 Controller ,用於接收使用者請求,有如下的一個方法。
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "/add", method = RequestMethod.GET)
public String addUser(User user, HttpServletRequest request) {
String token = request.getHeader("oauthToken");
return "hello, add user : " + user.getName();
}
……
}
基於上述兩個服務,使用者端 consumer 定義了一個 feign 使用者端用於請求伺服器端的服務
@FeignClient(name = "provider")
public interface UserFeignService {
@RequestMapping(value = "/user/add", method = RequestMethod.GET)
String addUser(User user);
……
}
用於 feign 使用 GET 無法直接傳遞 POJO,所以定義如下一個攔截器,在 apply 方法種處理請求並封裝成 POJO 傳送給伺服器端,本範例中,我們要封裝的是 User 物件
public class User {
private Long id;
private String name;
private int age;
// 省略 Get / Set 方法
}
定義的攔截器程式碼如下
@Component
public class FeignRequestInterceptor implements RequestInterceptor {
private final ObjectMapper objectMapper;
public FeignRequestInterceptor(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public void apply(RequestTemplate template) {
if (template.method().equals("GET") && template.body() != null) {
try {
JsonNode jsonNode = objectMapper.readTree(template.body());
template.body(null, StandardCharsets.UTF_8);
Map<String, Collection<String>> queries = new HashMap<>();
buildQuery(jsonNode, "", queries);
template.queries(queries);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void buildQuery(JsonNode jsonNode, String path, Map<String, Collection<String>> queries) {
if (!jsonNode.isContainerNode()) {
if (jsonNode.isNull()) {
return;
}
Collection<String> values = queries.computeIfAbsent(path, k -> new ArrayList<>());
values.add(jsonNode.asText());
return;
}
if (jsonNode.isArray()) { // 陣列節點
Iterator<JsonNode> it = jsonNode.elements();
while (it.hasNext()) {
buildQuery(it.next(), path, queries);
}
} else {
Iterator<Map.Entry<String, JsonNode>> it = jsonNode.fields();
while (it.hasNext()) {
Map.Entry<String, JsonNode> entry = it.next();
if (StringUtils.hasText(path)) {
buildQuery(entry.getValue(), path + "." + entry.getKey(), queries);
} else { // 根節點
buildQuery(entry.getValue(), entry.getKey(), queries);
}
}
}
}
}
測試一下,分別啟動 register-server,provider,consumer 三個專案,使用 Postman 做如下請求
返回成功結果。
完整程式碼見:feign-usage
本文來自部落格園,作者:Grey Zeng,轉載請註明原文連結:https://www.cnblogs.com/greyzeng/p/16890723.html