Search code examples
springspring-cloudnetflix-feign

How to custom @FeignClient Expander to convert param?


Feign default expander to convert param:

final class ToStringExpander implements Expander {

    @Override
    public String expand(Object value) {
      return value.toString();
    }
  }

I want custom it to convert user to support GET param, like this

@FeignClient("xx")
interface UserService{

   @RequestMapping(value="/users",method=GET)
   public List<User> findBy(@ModelAttribute User user);
}

userService.findBy(user);

What can i do?


Solution

  • First,you must write a expander like ToJsonExpander:

    public class ToJsonExpander implements Param.Expander {
    
      private static ObjectMapper objectMapper = new ObjectMapper();
    
      public String expand(Object value) {
        try {
          return objectMapper.writeValueAsString(value);
        } catch (JsonProcessingException e) {
          throw new ExpanderException(e);
        }
      }
    }
    

    Second, write a AnnotatedParameterProcessor like JsonArgumentParameterProcessor to add expander for your processor.

    public class JsonArgumentParameterProcessor implements AnnotatedParameterProcessor {
    
      private static final Class<JsonArgument> ANNOTATION = JsonArgument.class;
    
      public Class<? extends Annotation> getAnnotationType() {
        return ANNOTATION;
      }
    
      public boolean processArgument(AnnotatedParameterContext context, Annotation annotation) {
        MethodMetadata data = context.getMethodMetadata();
        String name = ANNOTATION.cast(annotation).value();
        String method = data.template().method();
    
        Util.checkState(Util.emptyToNull(name) != null,
            "JsonArgument.value() was empty on parameter %s", context.getParameterIndex());
    
        context.setParameterName(name);
    
        if (method != null && (HttpMethod.POST.matches(method) || HttpMethod.PUT.matches(method) || HttpMethod.DELETE.matches(method))) {
          data.formParams().add(name);
        } else {
          `data.indexToExpanderClass().put(context.getParameterIndex(), ToJsonExpander.class);`
          Collection<String> query = context.setTemplateParameter(name, data.template().queries().get(name));
          data.template().query(name, query);
        }
    
        return true;
      }
    }
    

    Third,add it to Feign configuration.

      @Bean
      public Contract feignContract(){
        List<AnnotatedParameterProcessor> processors = new ArrayList<>();
        processors.add(new JsonArgumentParameterProcessor());
        processors.add(new PathVariableParameterProcessor());
        processors.add(new RequestHeaderParameterProcessor());
        processors.add(new RequestParamParameterProcessor());
        return new SpringMvcContract(processors);
      }
    

    Now, you can use @JsonArgument to send model argument like:

    public void saveV10(@JsonArgument("session") Session session);