8

Feign 实现 GET 方法传递 POJO - Grey Zeng

 1 year ago
source link: https://www.cnblogs.com/greyzeng/p/16890723.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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 : 仅作注册中心,无其他业务方法
      • pom.xml
    • provider : 服务端端模块
      • pom.xml
    • consumer : 客户端模块
      • 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 做如下请求

image

返回成功结果。

完整代码见:feign-usage

参考资料#

重新定义 Spring Cloud 实战


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK