Feign客户端参数传递

2018/07/09 SpringCloud
  • 传基本类型参数

    使用@RequestParam,注意注解的value参数不可少,代码如下:

    @FeignClient(value = "demo-service", fallback = DemoServiceFallback.class) 
    public interface DemoService {
    
      @RequestMapping("/demo-service/test1")
      public String test1(@RequestParam(value = "userName") String userName, @RequestParam(value = "age") int age);
    }
    
  • 传Map传参数

    参数太多的时候,上面的方式就要写一堆,可以直接上map
    注意,需要加@RequestParam注解,但不需要加注解的value参数

    @RequestMapping("/demo-service/test4")
    public String test4(@RequestParam Map<String,Object> userMap);
    
  • 传对象

    Fegin传对象的时候,需要加@RequestBody注解,如下:

    @RequestMapping("/demo-service/test3")
    public String test3(@RequestBody DemoServiceUser user);
    

    注意,服务提供者的Controller的接收参数前也需要加@RequestBody注解

    @RequestMapping("/test3")
    public String test3(@RequestBody(required = false) User user, HttpServletRequest request) {
      return "[test3]userName=" + user.getUserName() + ", age=" + user.getAge();
    }
    

    @RequestBody接收的是一个Json对象的字符串,而不是一个Json对象
    如果这时候要使用postman直接请求上面的test3接口,那么需要将Content-Type修改为application/json image
    这样做虽然解决了feign传对象的问题,但是直接请求/test3接口就麻烦了,不能直接使用form-data的形式了。 这里使用RequestInteceptor来解决。

  • 传对象 + RequestInteceptor

    处理思路:feign发请求的时候,将json body转成query。
    服务提供者的Controller的接收参数前==不需要@RequestBody注解==
    在上例DemoService所在的项目中增加如下代码即可:

    @Configuration
    public class YryzRequestInterceptor implements RequestInterceptor {
      @Autowired
      private ObjectMapper objectMapper;
    
      @Override
      public void apply(RequestTemplate template) {
          // feign 不支持 GET 方法传 POJO, json body转query
          if (template.method().equals("GET") && template.body() != null) {
              try {
                  JsonNode jsonNode = objectMapper.readTree(template.body());
                  template.body(null);
    
                  Map<String, Collection<String>> queries = new HashMap<>();
                  buildQuery(jsonNode, "", queries);
                  template.queries(queries);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
    
      private void buildQuery(JsonNode jsonNode, String path, Map<String, Collection<String>> queries) {
          if (!jsonNode.isContainerNode()) { // 叶子节点
              if (jsonNode.isNull()) {
                  return;
              }
              Collection<String> values = queries.get(path);
              if (null == values) {
                  values = new ArrayList<>();
                  queries.put(path, values);
              }
              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);
                  }
              }
          }
      }
    }
    
  • 参考资料

    https://github.com/spring-cloud/spring-cloud-netflix/issues/1047

Search

    欢迎关注我的微信公众号

    钢仔

    Table of Contents