Feign学习笔记
项目依赖
feign需要依赖eureka客户端。目前一定需要连上eureka,所以目前还没找到可不走euraka直连的方法(已有办法,看下篇博客),所以现在就走eureka好了。
依赖如下
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-transprot-native-epoll</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
启动类
启动类其实并没有什么,只是@EnableEurekaClient 启动euraka客户端,@EnableFeignClients 启动feign客户端
@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
public class FeignMain {
public static void main(String[] args) {
SpringApplication.run(FeignMain.class, args);
}
}
需要注意的是,使用euraka是需要在application.yml中配置spring.application.name: xxxxx。
此处只贴出了feign客户端的application.yml。服务提供方的feign就不贴了。
server.port: 10103
spring:
application:
name: feign
eureka:
client:
healthcheck:
enabled: true
serviceUrl:
defaultZone: http://xx.xx.xx.xx:10101/eureka/
instance:
instance-id: ${spring.cloud.client.ipAddress}:${server.port}
prefer-ip-address: true
lease-expiration-duration-in-seconds: 30
lease-renewal-interval-in-seconds: 10
FeignClient
使用接口定义,然后加上@FeignClient(“服务提供放名称”)即可使用。其他语法与spring mvc相同
@FeignClient("rest-server")
public interface OrdinaryService {
@RequestMapping(value = "/getNum")
int getRandomInt();
}
实战细节
FeignClient默认RequestMethod是GET
@RequestMapping(value = "/getNum", method = RequestMethod.GET)
public int getRandomInt() {
Random random = new Random();
Integer num = random.nextInt(100);
logger.info("getRandomInt:" + num);
return num;
}
客户端使用如下方法调用
@RequestMapping(value = "/getNum")
int getRandomInt();
如果将服务提供方的方法改为post,测试时会提示找不到方法。
默认参数使用RequestBody
GET转换为POST的是有比较复杂的原因,也有方法可以避免。后面详解。
原因简介:因为此处参数中没有加入注释所以参数是用的RequestBody。
服务提供方方法
@RequestMapping(value = "/addOne", method = RequestMethod.POST)
public int addOne(@RequestBody int num) {
return num + 1;
}
客户端使用如下方法调用
@RequestMapping(value = "/addOne")
int addOne(int num);
GET请求被转换为POST
如果上个方法中客户端调用没有写method = RequestMethod.POST,这里与默认使用GET方法有冲突?不,因为feign默认使用的HTTPClient是java自带的。这个http客户端会检查body中是否携带参数,如果有就会吧GET请求转换为POST。
将上面代码改为:
服务提供方方法
@RequestMapping(value = "/addOne", method = RequestMethod.GET)
public int addOne(@RequestBody int num) {
return num + 1;
}
客户端使用如下方法调用
@RequestMapping(value = "/addOne", method = RequestMethod.GET)
int addOne(int num);
结果出现
feign.FeignException: status 405 reading OrdinaryService#addOne(int); content:
{"timestamp":1501516961283,"status":405,"error":"Method Not Allowed","exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'POST' not supported","path":"/addOne"}
避免GET请求被转换成POST请求
实际上上面的方法将GET改为POST就可以正常使用,但是实际上开发中这样写还是非常别扭。比如,我有如下接口。
服务提供方方法
@RequestMapping(value = "/addTwo", method = RequestMethod.GET)
public int addTwo(int num) {
return num + 1;
}
这么正常的方法,是在找不出理由去修改成为post。实际上调用方也是支持的
客户端使用如下方法调用
@RequestMapping(value = "/addTwo", method = RequestMethod.GET)
int addTwo(@RequestParam("num") int num);
此处有坑
RequestParam中必须填写,如果不填写会出现
Caused by:java.lang.IllegalStateException:QueryMap parameter must be a map:class ....
nested exception is java.lang.IllegalStateException: RequestParam.value() was empty on parameter 0
异常信息都是只截取了一半,但是都是关键信息
第一个异常是spring-cloud-netflix-core-1.2.6.RELEASE.jar会出现的。
第二个异常是spring-cloud-netflix-core-1.3.2.RELEASE.jar出现的。
其他版本应该差不多,虽然进化了好几个版本,但是主要问题依旧在于feign重写了RequestParam注解但是没有给予value默认值引起的。
所以使用RequestParam必须给予值,因为没有默认值