Spring Cloud OpenFeign 创建动态查询参数失败

Posted

技术标签:

【中文标题】Spring Cloud OpenFeign 创建动态查询参数失败【英文标题】:Spring Cloud OpenFeign Failed to Create Dynamic Query Parameters 【发布时间】:2019-05-01 23:28:12 【问题描述】:

Spring cloud openFeign 无法创建动态查询参数。它抛出异常是因为 SpringMvcContract 试图找到不存在的 RequestParam 值属性。

java.lang.IllegalStateException: RequestParam.value() was empty on parameter 0


@RequestMapping(method = RequestMethod.GET, value = "/orders")
Pageable<Order> searchOrder2(@RequestParam CustomObject customObject);

我尝试使用@QueryMap 而不是@RequestParam,但@QueryMap 不会生成查询参数。

顺便说一句@RequestParam Map&lt;String, Object&gt; params 方法参数可以很好地生成动态查询参数。

但我想使用一个自定义对象,其中 feign 客户端可以从对象的属性中生成动态查询参数。

【问题讨论】:

AFAIK,Spring 在将查询参数绑定到自定义 DTO 时不需要 RequestParam 注释。 (Ref)。您是否尝试过不使用 RequestParam 注释?这个合同在服务器端工作正常吗?如果这个接口只是为了在 Feign 端生成客户端而创建的,你有没有尝试过其他格式?动态查询参数是什么意思? AFAIK,http 规范没有动态查询参数之类的东西。 感谢@bhdrkn 的帮助。请看我的回答。 【参考方案1】:

spring-cloud-starter-feign 有一个 open issue 用于支持 pojo 对象作为请求参数。因此,我使用了一个请求拦截器,它从 feign 方法中获取对象并从其字段中创建 url 的查询部分。感谢@charlesvhe

public class DynamicQueryRequestInterceptor implements RequestInterceptor 

private static final Logger LOGGER = LoggerFactory.getLogger(DynamicQueryRequestInterceptor.class);

private static final String EMPTY = "";

@Autowired
private ObjectMapper objectMapper;

@Override
public void apply(RequestTemplate template) 
    if ("GET".equals(template.method()) && Objects.nonNull(template.body())) 
        try 
            JsonNode jsonNode = objectMapper.readTree(template.body());
            template.body(null);

            Map<String, Collection<String>> queries = new HashMap<>();
            buildQuery(jsonNode, EMPTY, queries);
            template.queries(queries);
         catch (IOException e) 
            LOGGER.error("IOException occurred while try to create http query");
        
    


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);
            
        
    

【讨论】:

【参考方案2】:

来自Spring Cloud OpenFeign Docs:

Spring Cloud OpenFeign提供了等价的@SpringQueryMap注解,用于将POJO或Map参数注解为查询参数映射

所以你的代码应该是:

@RequestMapping(method = RequestMethod.GET, value = "/orders")
Pageable<Order> searchOrder2(@SpringQueryMap @ModelAttribute CustomObject customObject);

【讨论】:

非常感谢您。我删除了@RequestParam,因为它将对象添加到 url。 @mstzn,已删除

以上是关于Spring Cloud OpenFeign 创建动态查询参数失败的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud OpenFeign

Spring Cloud OpenFeign文档

SpringBoot引入openfeign 报错:spring-cloud-starter-openfeign:unknown

OpenFeign调用服务

SpringCloud - Spring Cloud Alibaba 之 Sentinel DashBoard ;RestTemplate;OpenFeign

spring cloud openfeign 源码