8

I am writing a REST client using Feign. There is an endpoint which can be generalized by parameterizing the path. But based on the path I can get a different type of response.

So I am trying to use a single method using generic. Since I must tell the method on the return type, I am parameterizing the type of the return value, like below,

@RequestLine("GET /objects/{type}/{model_id}")
public <T> Entity<T> getObject(
            @Param("type") String theObjectType, @Param("model_id") String theModelId,
            Class<T> theResponseClass);

But the problem is, Feign will use theResponseClass as body. How can I achieve a generic feign client method?

Kannan Ramamoorthy
  • 3,980
  • 9
  • 45
  • 63
  • Feign sees all non-`@Param`-annotated (there are a couple more annotations it recognizes as well) parameters as "body" params (there can be only 1) which it will encode into the message it sends. You could try: `public BaseClass getObject(@Param("type") String type, @Param("id") String id);`. If your object types don't share a base class or interface, then just use `Object`. You might also need to use your own custom `Decoder` that will need to know how to instantiate your objects based on the contents of the returned message body. – Shadow Man Mar 30 '18 at 01:44
  • @Kannan did u find any solution? – kashi viswanath May 18 '20 at 18:57
  • @kashiviswanath Nope.. – Kannan Ramamoorthy May 19 '20 at 01:18

2 Answers2

1

You could just use Feigns' generic Response type. Sadly it's not typesafe and requires to return the body as inputStream or byte[].

Like this:

  @RequestLine("GET /objects/{type}/{model_id}")
  public Response getMyData(@Param("model_id") String theModelId)
  {
    return Response.Builder.body(response).build();
  }
Laess3r
  • 944
  • 1
  • 9
  • 13
-1

when it comes to Generic Responses it was always hard to make Feign returns a generic Response, Your only Option is to Define String response then map it with mapstruct :

 @RequestLine("GET /objects/{type}/{model_id}")
    String getObject(@Param("type") String theObjectType, @Param("model_id") String theModelId);

then define in a utility class like:

public final class JsonUtils {

private JsonUtils() {

}

 @SneakyThrows
public static <T> T jsonToObject(final String jsonString, final Class<T> clazz) {
    final ObjectMapper objectMapper = buildObjectMapper();
    return objectMapper.readValue(jsonString, clazz);
}

 public static ObjectMapper buildObjectMapper() {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
    objectMapper.registerModule(new ParameterNamesModule());
    objectMapper.registerModule(new Jdk8Module());
    objectMapper.registerModule(new JavaTimeModule());
    return objectMapper;

}

}