最全面的总结spring cloud Feign的常见问题

本文踩坑有二:

  1. not annotated with HTTP method type (ex. GET, POST)
  2. FeignException$BadRequest: status 400

1.not annotated with HTTP method

报错内容如下:

java.lang.IllegalStateException:
 Method XXX not annotated with HTTP method type (ex. GET, POST)

Feign可以使用自带注解@RequestLine以及spring的注解@RequestMapping、@GetMapping等

几个需要注意的点:

  • 使用spring的注解@RequestMapping需要指定method属性以及value(路径信息)
  • 看了很多其他博客说不可以使用@GetMapping等这类注解,本人亲测,可以使用
  • 第三点比较重要的
    如果使用Feign自带注解@RequestLine,需要修改默认配置。spring cloud netflix默认为feign提供的默认bean如下:
Decoder feignDecoder:ResponseEntityDecoder(其中包含SpringDecoder)

Encoder feignEncoder:SpringEncoder

Logger feignLogger:Slf4jLogger

Contract feignContract:SpringMvcContract

Feign.Builder feignBuilder:HystrixFeign.Builder

Client feignClient:如果Ribbon启用,则为LoadBalancerFeignClient,否则将使用默认的feign客户端。

看上方的Contract feignContract:SpringMvcContract,说明feign默认使用springmvc的协议或者约定,使用@RequestMapping的注解,若需要使用@RequestLine,需要修改约束配置,如下

@Configuration
public class Configuration {
    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }
}

在feignclient注解里配置

@FeignClient(value = "client", url = "${XXX}", configuration = Configuration .class)

2.status 400

报错内容如下:

FeignException$BadRequest: status 400 reading xxService#xxmethod(String, Interger)

看错误码400,http错误码400主要有两种形式:
1、bad request 意思是 “错误的请求”;
2、invalid hostname 意思是 “不存在的域名”。
400 Bad Request 是由于明显的客户端错误(例如,格式错误的请求语法,太大的大小,无效的请求消息或欺骗性路由请求),服务器不能或不会处理该请求。

原因

查找其他博客得到结果有一下几类:

  • 传递的参数为空
  • 参数长度过长
  • header过长
  • RequestMapping没有指定method属性
  • 本人遇到的情况,header参数错误(下面讲解错误原因)

解决

对于第一种传参为空,可以指定参数require=false,如下

@RequestParam(value = "param",required = false) String param

还要注意的是如果使用rest风格的请求,路径有占位符,一定要使用@PathVariable注解,这也是很多人踩过的坑。
对于第二种问题,是由于springboot内嵌tomcat对参数长度限制是8K,可以修改配置进行解决

扫描二维码关注公众号,回复: 10032005 查看本文章
server.max-http-header-size=20480

第三种情况与第二种情况一致。
第四种情况直接指定方法就好。

本人踩坑复现

本人遇到的问题是header设置的问题。由于需要使用token进行验证,而且不同的请求对应不同的token,导致无法使用静态header进行指定,于是使用修改配置的方式进行,直接修改restTemplate.

@Configuration
public class AuthorityConfig implements RequestInterceptor{
    @Override
    public void apply(RequestTemplate template) {
        SecurityContext securityCxt = SecurityContextHolder.getContext();
        XXX token = XXX securityCxt.getAuthentication();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //添加token
        template.header(Constants.TOKEN, request.getHeader(token.getToken()));
        //template.headers().remove("Content-Type");
//        Map<String, Collection<String>> headers = new HashMap<>();
//        ArrayList<String> headerToken = new ArrayList<>();
//        headerToken.add(request.getHeader(token.getToken()));
//        headers.put(Constants.TOKEN, headerToken);
//        headers.put("Content-Type", new ArrayList(){{add("application/x-www-form-urlencoded");}});
        template.headers(new HashMap<>());
        template.header("Content-Type", "application/x-www-form-urlencoded");
        template.header(Constants.TOKEN_TEACHER, token.getToken());
        System.out.println("headers is : "+template.headers());
    }
}

上方代码看倒数第四行,我设置了一个空的map,之后才进行的其他header的添加。这是踩坑之后才发现得这么做的。
看我注释掉的代码发现,template.header()需要一个string和一个集合,也就是说,template会把key相同的属性放到一个集合里。这似乎也很正常,坑的是它本身有一些默认的header,如果你添加了同名的header后只是加入到了一个集合而不是覆盖,导致我注释的代码添加Content-Type后,该key对应的header是

[application/json,application/x-www-form-urlencoded]

这就很尴尬,服务器因为这个拒绝了我。查看其源码实现

public RequestTemplate headers(Map<String, Collection<String>> headers) {
    if (headers != null && !headers.isEmpty()) {
      headers.forEach(this::header);
    } else {
      this.headers.clear();
    }
    return this;
  }

发现传入null或者空可以实现clear(),所以我传了空先进行清除后再逐个添加,这样解决了400的问题。

推荐阅读:feign实战中header的巧妙处理
以上是这两天的踩坑集锦,有误之处请指正,不胜感激~

发布了51 篇原创文章 · 获赞 1 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/cuiyaocool/article/details/104808742
今日推荐