Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Cloud OpenFeign Failed to Create Dynamic Query Parameters

Spring cloud openFeign can't create dynamic query parameters. It throws below exception because SpringMvcContract tries to find the RequestParam value attribute which doesn't exist.

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


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

I tried using @QueryMap instead of @RequestParam but @QueryMap does not generate query parameters.

Btw @RequestParam Map<String, Object> params method parameter works fine to generate a dynamic query parameter.

But I want to use a custom object in which the feign client can generate dynamic query parameters from the object's attributes.

like image 922
mstzn Avatar asked Nov 29 '18 20:11

mstzn


People also ask

How do you pass query parameters in feign client?

Query parameters can be configured in Feign clients by using the @RequestParam annotation from the Spring web framework on method arguments which should be passed as query parameters when calling the remote service.

What is Spring Cloud OpenFeign?

In this tutorial, we're going to describe Spring Cloud OpenFeign — a declarative REST client for Spring Boot apps. Feign makes writing web service clients easier with pluggable annotation support, which includes Feign annotations and JAX-RS annotations.

Is feign client blocking?

Reactive Feign is great choice for the implementation of non-blocking API clients. It is a reactive version of OpenFeign which supports the creation of API clients without the need to writing implementation code. By just defining interface and configuration, development of API clients can be done effortlessly.


2 Answers

From Spring Cloud OpenFeign Docs:

Spring Cloud OpenFeign provides an equivalent @SpringQueryMap annotation, which is used to annotate a POJO or Map parameter as a query parameter map

So your code should be:

@RequestMapping(method = RequestMethod.GET, value = "/orders")
Pageable<Order> searchOrder2(@SpringQueryMap @ModelAttribute CustomObject customObject);
like image 145
StasKolodyuk Avatar answered Sep 17 '22 23:09

StasKolodyuk


spring-cloud-starter-feign has a open issue for supporting pojo object as request parameter. Therefore I used a request interceptor that take object from feign method and create query part of url from its fields. Thanks to @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);
            }
        }
    }
}

}

like image 39
mstzn Avatar answered Sep 21 '22 23:09

mstzn