4

Does Feign client support optional request param?

For example, I have an endpoint, but I am not finding a way to actually make the param1 optional using feign client.

@GetMapping(path = "endpoint1")
ResponseEntity request(@RequestParam(name = "param1", required = false, defaultValue = "key") String param1){}
krmanish007
  • 6,749
  • 16
  • 58
  • 100

3 Answers3

3

Since you are using Spring Cloud Feign I find the easiest way to implement optional parameters is using a DTO, and optionally implementing a builder pattern (Lombok can help to make that less verbose).

The DTO will make the parameter in the request optional (when null), not the method signature. Optional parameters to methods should be -almost- impossible but some alternatives can be found here.

The builder pattern will make the initialization parameters optional (or mandatory) details can be found here. The builder can also be used to set defaults. Lombok details can be found here. Alternatively a builder can built manually. Starting at wikipedia. Some example pure Java code.

Code might look like this:

@GetMapping(path = "/endpoint1") //Not starting with / does not look ok...
ResponseEntity request(@SpringQueryMap MyDTO);

//MyDTO

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class MyDTO {

  private String param1;
  private String param2;
  private String param3;
}

//when using

request(MyDTO.Builder().build()); //use all defaults
request(MyDTO.Builder().param1("foo").build()); //param1 set, all other defaults
request(MyDTO.Builder().param1("foo").param2("bar").build()); //param1 and param2 set
gbarco
  • 129
  • 1
  • 3
3

I managed to use Optional request params with Feign by creating a customised FeignFormatterRegistrar. Here is the code:

package feignformatters;

import org.springframework.cloud.openfeign.FeignFormatterRegistrar;
import org.springframework.format.FormatterRegistry;
import org.springframework.stereotype.Component;

import java.util.Optional;

@Component
public class OptionalFeignFormatterRegistrar implements FeignFormatterRegistrar {
    
    @Override
    public void registerFormatters(FormatterRegistry registry) {
        registry.addConverter(
            Optional.class, 
            String.class, 
            optional -> {
                if (optional.isPresent())
                    return optional.get().toString();
                else
                    return "";
            });
    }
}

The following client started working fine with the previous component loaded in the project:

@FeignClient("book-service")
public interface BookServiceClient {

    @GetMapping("/books")
    public List<Book> getBooks(
        @RequestParam("page") Optional<Integer> pageNum,
        @RequestParam("size") Optional<Integer> pageSize,
        @RequestParam("reader") Optional<Long> readerId);
}
dbaltor
  • 2,737
  • 3
  • 24
  • 36
0

You can use simple Map<String, String> annotated with @SpringQueryMap then you can pass as many optional/mandatory parameters as you want.

@GetMapping("/task")
ResponseEntity<List<TaskResponse>> getTasks(@SpringQueryMap Map<String, String> queryParameters);

Make sure also to validate the parameters you pass to the map if some of them aren't optional.

Something like this:

List<TaskResponse> getTasks(@NotBlank final String mandatoryParam1, final String optionalParam2) {

        Map<String, String> parameters = new HashMap<>();
        parameters.put("mandatoryParam1", mandatoryParam1);
        Optional.ofNullable(optionalParam2)
                .ifPresent(nonNullParam -> parameters.put("optionalParam2", nonNullParam));

        return yourFiegnApiClient.getTasks(parameters).getBody();
    }