Feign使用小记

Posted by Jaimin on August 1, 2019

简介

Feign 是一个声明式的 Web Service 客户端。它的出现使开发 Web Service 客户端变得很简单。使用 Feign 只需要创建一个接口加上对应的注解,比如:@FeignClient 注解。 Spring Cloud Open Feign 对 Feign 进行增强支持 Spring Mvc 注解,可以像 Spring Web 一样使用 HttpMessageConverters 等。

简而言之,Feign用来做服务调用,在Spring Cloud 中使用 Feign,可以更简单的使用 HTTP 请求访问远程服务。

基本使用

引入依赖

1
2
3
4
    <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>

启动类加入以下注解

1
2
/** 激活Feign客户端 */
@EnableFeignClients 

定义一个Feign客户端

1
2
3
4
5
6
7
8
@FeignClient(name = "testAPIClient",url = "${api.url}",configuration = FeignConfiguration.class)
public interface TestAPIClient {
	@RequestMapping(path = "/add", 
    method =RequestMethod.POST,
	consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, 
    produces = MediaType.APPLICATION_JSON_VALUE)
    public String testAdd (@RequestParam("name") String name);
}

produces是注解@requestMapping注解里面的属性项,它的作用是指定返回值类型,不但可以设置返回值类型还可以设定返回值的字符编码;

consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;

@FeignClient 注解

  • configuration:Feign 配置类,可以自定义 Feign 的 Encoder、Decoder、LogLevel、Contract。
  • name:指定 Feign Client 的名称,如果项目使用了 Ribbon,name 属性会作为微服务的名称,用于服务发现。
  • url:url 一般用于调试,可以手动指定 @FeignClient 调用的地址。

测试类,调用Feign客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
@RequestMapping(
        value = "/test"
)
public class TestController {

    @Resource
    private TestAPIClient testAPIClient;

    @RequestMapping(
            value = "/testFeign",
            method = RequestMethod.POST
    )
    String testFeign(@RequestParam("name") String name) {
        return testAPIClient.testAdd(ame);
    }
}

踩坑记录

今天在项目中遇到了一个问题,在Feign调用时出现了异常,

feign.FeignException: status 400 reading xxx…

经过分析日志与百度,发现是参数超长了,原来的Feign客户端实现方式是这样

1
2
@RequestMapping(path = "/xxx", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
String test(@RequestParam("data") String data);

在没有改变默认Encoder的设置的情况下(没有指定@RequestMapping的consumes属性),FeignClient 对于简单类型的参数,例如String,Integer这些包装类,这种类型将被编码为form表单参数,即便写了method=RequestMethod.POST,但是参数没有出现在Body中,而是在URL上,会存在超长的情况

解决方案

引入依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
            <groupId>io.github.openfeign.form</groupId>
            <artifactId>feign-form</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign.form</groupId>
            <artifactId>feign-form-spring</artifactId>
            <version>3.4.1</version>
        </dependency>

修改Feign客户端

1
2
3
4
5
6
7
@FeignClient(name = "xxx",url = "${api.url}",configuration = FeignConfiguration.class)
public interface xxx {
    @RequestMapping(path = "/xxx", method = RequestMethod.POST,
 consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
 produces = MediaType.APPLICATION_JSON_VALUE)
 String test(Map<String,?> data);
}
  1. 属性consumes = {"application/x-www-form-urlencoded"},指定Feign以表单方式提交
  2. 参数使用Map<String, ?>形式 (不再是@RequestParam("parameter")这种形式);

配置类,注册一个feignFormEncoder bean组件的方式到应用上下文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Configuration
public class FeignConfiguration {

    @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters;
     // 注意这里方法名称,也就是bean的名称是什么不重要,
    // 重要的是返回类型要是 Encoder 并且实现类必须是 FormEncoder 或者其子类
    // new一个form编码器,实现支持form表单提交
    @Bean
    @Primary
    @Scope(SCOPE_PROTOTYPE)
    public Encoder feignFormEncoder() {
        return new SpringFormEncoder(new SpringEncoder(messageConverters));
    }
	//关闭Feign日志
    @Bean
    public Logger.Level feignLoggerLevel(){
        return Logger.Level.NONE;
    }
}

至此,通过接口调用就不会提示400的错误了

参考地址

感谢以下大佬的链接提供学习思路

Feign 基本使用

https://blog.csdn.net/wo18237095579/article/details/83343915

SpringCloud Feign Post表单请求

https://www.jianshu.com/p/08a5dd04093e

@EnableFeignClients 客户端详细

https://www.jianshu.com/p/62d6c4779c01

使用Feign实现Form表单提交

https://blog.csdn.net/weixin_33962923/article/details/89651126

官方指导

https://github.com/OpenFeign/feign-form

Spring Cloud : feign 客户端使用FORM形式POST数据

https://blog.csdn.net/andy_zhang2007/article/details/87860407