Feign 实现 GET 方法传递 POJO

Feign 实现 GET 方法传递 POJO

作者:Grey

原文地址:

博客园:Feign 实现 GET 方法传递 POJO

CSDN:Feign 实现 GET 方法传递 POJO

需求

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

项目结构和说明

  • feign-usage:父项目名称
    • register-server : 仅作注册中心,无其他业务方法
      • src/
      • pom.xml
    • provider : 服务端端模块
      • src/
      • pom.xml
    • consumer : 客户端模块
      • src/
      • pom.xml
    • pom.xml:父项目 pom 配置

代码说明

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 实现 GET 方法传递 POJO

返回成功结果。

完整代码见:feign-usage

参考资料

重新定义 Spring Cloud 实战

发表评论

评论已关闭。

相关文章