• 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();
        }
    
    ……
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    基于上述两个服务,客户端 consumer 定义了一个 feign 客户端用于请求服务端的服务

    @FeignClient(name = "provider")
    public interface UserFeignService {
    
        @RequestMapping(value = "/user/add", method = RequestMethod.GET)
        String addUser(User user);
    ……
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    用于 feign 使用 GET 无法直接传递 POJO,所以定义如下一个拦截器,在 apply 方法种处理请求并封装成 POJO 发送给服务端,本实例中,我们要封装的是 User 对象

    public class User {
    
        private Long id;
        private String name;
        private int age;
    
      // 省略 Get / Set 方法
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    定义的拦截器代码如下

    
    @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);
                    }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    测试一下,分别启动 register-server,provider,consumer 三个项目,使用 Postman 做如下请求

    image

    返回成功结果。

    完整代码见:feign-usage

    参考资料

    重新定义 Spring Cloud 实战

  • 相关阅读:
    js教程(12)——本地储存
    FFmpeg 中 -f 命令参数详解
    Go的简单入门:开始使用多模块的工作空间
    基于STM32+华为云设计的智能鱼缸
    正则表达式
    B端产品经理和C端产品经理,做哪个更好?
    【笔记】pyinstaller 封装含GUI的背景图及软件icon
    迅为RK3568开发板学习之Linux驱动篇第十三期输入子系统
    Flutter 错误must be a valid Dart package name
    uniapp+地图 实现目的地导航
  • 原文地址:https://blog.csdn.net/hotonyhui/article/details/127857050