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.
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.
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.
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.
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);
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);
}
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With