使用Feign实现声明式REST调用
之前示例中是使用RestTemplate实现REST API调用的,代码大致如下:
public User findById(@PathVariable Long id){
return this.restTemplate.getForObject("http://microservice-provider-user/"+id,User.class);
}
由代码可知,我们是使用拼接字符串的方式构造URL的,该URL只有一个参数。然而在现实中,URL中往往有多个参数。如果这时还使用这种方式构造URL,那么就会变得很低效,并且难以维护。
举个例子,想要请求这样的URL
http://localhost:8010/search?name=张三&username=account1&age=20
若使用拼接字符串的方式构建请求URL,那么代码可编写为
public User[] findById(String name,String username,Integer age){
Map<String,Object> paramMap= Maps.newHashMap();
paramMap.put("name",name);
paramMap.put("username",username);
paramMap.put("age",age);
return this.restTemplate.getForObject("http://microservice-provider-user/search?name={name}&username={username}&age={age}",User[].class,paramMap);
}
在这里,URL仅包含3个参数。如果URL更加复杂,例如有10个以上的参数,那么代码会变得难以维护。
Feign简介
Feign是Netflix开发的声明式、模板化的HTTP客户端,其灵感来自Retrofit、JAXRS-2.0以及WebSocket。Feign可帮助我们更加便捷、优雅地调用HTTP API。
在Spring Cloud中,使用Feign非常简单——创建一个接口,并在接口上添加一些注解,代码就完成了。Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等。
Spring Cloud对Feign进行了增强,使Feign支持了Spring MVC注解,并整合了Ribbon和Eureka,从而让Feign的使用更加方便。
为服务消费者整合Feign
之前的电影微服务是使用RestTemplate(通过整合Ribbon实现负载均衡)调用 RESTful API的,这里让电影微服务使用Feign,实现声明式的RESTful API调用。
1)复制项目microservice-consumer-movie,将ArtifactId修改为microservice-consumer-movie-feign。
2)添加Feign的依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
3)创建一个Feign接口,并添加@FeignClient注解
package com.example.feign;
import com.example.entity.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
public User findById(@PathVariable("id") Long id);
}
@FeignClient注解中的microservice-provider-user是一个任意的客户端名称,用于创建Riddon负载均衡器。在本例中,由于使用了Eureka,所以Ribbon会把microservice-provider-user解析成Eureka Server服务注册表中的服务。当然,如果不想使用Eureka,可使用service.ribbon.listofServers属性配置服务器列表。
还可使用url属性指定请求的URL(URL可以是完整的URL或者主机名),例如@FeignClient(name=“microservice-provider-user”,url=“http://localhost:8000/”)。
4)修改Controller代码,让其调用Feign接口
@Autowired
private UserFeignClient userFeignClient;
@GetMapping("/user/{id}")
public User findById(@PathVariable Long id){
return this.userFeignClient.findById(id);
}
5)修改启动类,为其添加@EnableFeignClient注解
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableFeignClients
public class MicroserviceConsumerMovieFeignApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceConsumerMovieFeignApplication.class, args);
}
}
这样,电影微服务就可以用Feign去调用用户微服务的API了
微服务
1)启动microservice-discovery-eureka
2)启动2个或更多microservice-provider-user实例。
3)启动microservice-consumer-movie-feign。
4)多次访问http://localhost:8010/user/1,返回如下结果
{"id":1,"username":"account1","name":"张三","age":20,"balance":100.00}
两个用户微服务实例都会打印类似如下的日志
Hibernate: select user0_.id as id1_0_0_, user0_.age as age2_0_0_, user0_.balance as balance3_0_0_, user0_.name as name4_0_0_, user0_.username as username5_0_0_ from user user0_ where user0_.id=?
自定义Feign配置
很多场景下,我们需要自定义Feign的配置,例如配置日志级别、定义拦截器等。Spring Cloud Edgware允许使用Java代码或属性自定义Feign的配置,两种方式是等价的。
使用Java代码自定义Feign配置
在Spring Cloud中,Feign的默认配置类是FeignClientsConfiguration,该类定义了Fergin默认使用的编码器、解码器、所使用的契约等。
Spring Cloud允许通过注解@FeignClient的configuration属性自定义Feign的配置,自定义配置的优先级比FeignClientConfiguration要高。
在Spring Cloud文档中可看到以下段落,描述了Spring Cloud提供的默认配置。另外,有的配置尽管没有提供默认值,但是Spring也会扫描其中列出的类型(也就是说,这部分配置也能自定义)。
配置指定名称的Feign Client
由此可知,在Spring Cloud中,Feign默认使用的契约是SpringMvcContract,因此它可以使用Spring MVC的注解。下面来自定义Feign的配置,让它使用Feign自带的注解进行工作。
1)复制项目microservice-consumer-movie-feign,将ArtifactId修改为microservice-consumer-movie-feign-customizing.
2)创建Feign的配置类。
package com.example.config;
import feign.Contract;
import org.springframework.context.annotation.Bean;
public class FeignConfiguration {
@Bean
public Contract feignContract(){
return new feign.Contract.Default();
}
}
注:该类可以不写@Configuration注解;如果加了@Configuration注解,那么该类不能放在主程序上下文@ComponentScan所扫描的包中。
将契约改为feign原生的默认契约。这样就可以使用feign自带的注解了。@return默认的feign契约。
3)Feign接口修改为如下,使用@FeignClient的configuration属性指定配置类,同时,将findById上的Spring MVC注解修改为Feign自带的注解。
package com.example.feign;
import com.example.config.FeignConfiguration;
import com.example.entity.User;
import feign.Param;
import feign.RequestLine;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient(name = "microservice-provider-user",configuration = FeignConfiguration.class)
public interface UserFeignClient {
// @RequestMapping(value = "/{id}",method = RequestMethod.GET)
@RequestLine("GET /{id}")
public User findById(@Param("id") Long id);
}
类似地,还自定义Feign地编码器、解码器、日志打印,甚至为Feign添加拦截器。例如,一些接口需要进行基于HttpBasic地认证后才能调用,配置类可以这样写。
package com.example.config;
import feign.auth.BasicAuthRequestInterceptor;
import org.springframework.context.annotation.Bean;
public class FooConfiguration {
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor(){
return new BasicAuthRequestInterceptor("user","password");
}
}
1)启动microservice-discovery-eureka
2)启动2个或更多microservice-provider-user实例。
3)启动microservice-consumer-movie-feign-customizing。
4)访问http://localhost:8010/user/1,返回如下结果
在Spring Cloud Edgware中,Feign的配置类(例如本例中的FeignConfiguration类)无须添加@Configuration注解;如果加了@Configuration注解,那么该类不能存放在主应用程序上下文@ComponentScan所扫描的包中。否则,该类中的配置feign.Decoder、feign.Contract等配置就会被所有的@FeignClient共享。
为避免造成问题,最佳实践是不再指定名称的Feign配置类上添加@Configuration注解。
全局配置
注解@EnableFeignClients为我们提供了defaultConfiguration属性,用来指定默认的配置类,例如:
@EnableFeignClients(defaultConfiguration = DefaultRibbonConfig.class)
使用属性自定义Feign配置
从Spring Cloud Netflix 1.4.0开始(即从Spring Cloud Edgware)开始,Feign支持属性自定义。这种方式比使用Java代码配置的方式更加方便。
配置指定名称的Feign Client
对于指定名称的Feign Client,配置如下。
feign:
client:
config:
feignName:
#相当于Request.Options
connectTimeout: 5000
#相当于Request.Options
readTimeout: 5000
#配置Feign的日志级别,相当于代码配置方式中的Logger
loggerLevel: full
#Feign的错误解码器,相当于代码配置方式中的ErrorDecoder
errorDecoder: com.example.SimpleErrorDecoder
#配置重试,相当于代码配置方式中的Retryer
retryer: com.example.SimpleRetryer
#配置拦截器,相当于代码配置方式的RequestInterceptor
requestInterceptors:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
decode404: false
通用配置
上面讨论了如何配置指定名称的Feign Client,如果想配置所有的Feign Client,只需做如下配置
feign:
client:
config:
default:
#相当于Request.Options
connectTimeout: 5000
#相当于Request.Options
readTimeout: 5000
#配置Feign的日志级别,相当于代码配置方式中的Logger
loggerLevel: full
#Feign的错误解码器,相当于代码配置方式中的ErrorDecoder
errorDecoder: com.example.SimpleErrorDecoder
#配置重试,相当于代码配置方式中的Retryer
retryer: com.example.SimpleRetryer
#配置拦截器,相当于代码配置方式的RequestInterceptor
requestInterceptors:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
decode404: false
注:属性配置的方式比Java代码配置的方式优先级更高。如果你想让Java代码配置方法优先级更高,可使用这个属性:feign.client.default-to-properties=false。
手动创建Feign
在某些场景下,前文自定义Feign的方式满足不了需求,此时可使用Feign Builder API手动创建Feign
- 用户微服务的接口需要登陆后才能调用,并且对于相同的API,不同角色的用户有不同的行为。
- 让电影微服务中的同一个Feign接口使用不同的账号登录,并调用用户微服务的接口。
修改用户微服务
- 复制项目microservice-provider-user,将ArtufactId修改为microservice-provider-user-with-auth。
- 为项目添加以下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
- 创建Spring Security的配置类
package com.example.entity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
public class SecurityUser implements UserDetails {
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String password;
private String role;
public SecurityUser( String username, String password, String role) {
super();
this.username = username;
this.password = password;
this.role = role;
}
public SecurityUser() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
@Override
public String getUsername() {
return username;
}
@Override
public String getPassword() {
return password;
}
public void setRole(String role) {
this.role = role;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities=new ArrayList<GrantedAuthority>();
SimpleGrantedAuthority authority=new SimpleGrantedAuthority(this.role);
authorities.add(authority);
return authorities;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
package com.example.component;
import com.example.entity.SecurityUser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collection;
@Component
public class CustomUserDetailsService implements UserDetailsService {
/*
* 模拟两个账户:
* 1、账号是user,密码是password1,角色是user-role
* 2、账号是admin,密码是password2,角色是admin-role
* */
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Collection<GrantedAuthority> authorities=new ArrayList<GrantedAuthority>();
if ("user".equals(username)) {
return new SecurityUser("user","password1","user-role");
} else if ("admin".equals(username)) {
return new SecurityUser("admin","password2","admin-role");
} else {
return null;
}
}
}
package com.example.config;
import com.example.component.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//所有的请求都需要经过HTTP basic
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
@Bean
public PasswordEncoder passwordEncoder() {
//明文编码器,这是一个不作任何操作的密码编码器,是Spring提供给我们做明文测试的
return NoOpPasswordEncoder.getInstance();
}
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.userDetailsService).passwordEncoder(this.passwordEncoder());
}
}
代码中模拟了两个账号:user和admin,它们的密码分别是password1和password2,角色是user-role和admin-role
- 修改Controller,在其中打印当前登录的用户信息。
package com.example.controller;
import com.example.dao.UserRepository;
import com.example.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collection;
@RestController
public class UserController {
@Autowired
private UserRepository userRepository;
private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class);
@GetMapping(value = "/{id}")
public User findById(@PathVariable long id) {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if(principal instanceof UserDetails){
UserDetails user = (UserDetails) principal;
Collection<? extends GrantedAuthority>conllection = user.getAuthorities();
for(GrantedAuthority c : conllection){
//打印当前登录用户的信息
UserController.LOGGER.info("当前用户是{},角色是{}",user.getUsername(),c.getAuthority());
}
}else{
//do other things
}
User findOne = this.userRepository.findOne(id);
return findOne;
}
}
测试
1)启动microservice-discovery-eureka。
2)启动microservice-provider-user-with-auth。
3)访问http://localhost:8000/1,将弹出登录对话框。
4)使用user/password1登录。
5)使用user/password2登录。
修改电影微服务
1)复制项目microservice-consumer-movie-feign,将ArtifactId修改为microservice-consumer-movie-feign-manual。
2)去掉Feign接口UserFeignClient上的@FeignClient注解
3)去掉启动类上的@EnableFeignClients注解。
4)修改Controller
package com.example.controller;
import com.example.entity.User;
import com.example.feign.UserFeignClient;
import feign.Client;
import feign.Contract;
import feign.Feign;
import feign.auth.BasicAuthRequestInterceptor;
import feign.codec.Decoder;
import feign.codec.Encoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.feign.FeignClientsConfiguration;
import org.springframework.context.annotation.Import;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@Import(FeignClientsConfiguration.class)
@RestController
public class MovieController {
private static final Logger LOGGER = LoggerFactory.getLogger(MovieController.class);
private UserFeignClient userUserFeignClient;
private UserFeignClient adminUserFeignClient;
@Autowired
public MovieController(Decoder decoder, Encoder encoder, Client client, Contract contract){
//这边的decoder、encoder、client、contract,可以Debug看看是什么实例
this.userUserFeignClient = Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract)
.requestInterceptor(new BasicAuthRequestInterceptor("user","password1")).target(UserFeignClient.class,"http://microservice-provider-user/");
this.adminUserFeignClient = Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract)
.requestInterceptor(new BasicAuthRequestInterceptor("admin","password2")).target(UserFeignClient.class,"http://microservice-provider-user/");
}
@GetMapping("/user-user/{id}")
public User fincByIdUser(@PathVariable Long id){
return this.userUserFeignClient.findById(id);
}
@GetMapping("/user-admin/{id}")
public User fincByIdAdmin(@PathVariable Long id){
return this.adminUserFeignClient.findById(id);
}
}
其中,@Import导入的FeignClientsConfiguration是Spring Cloud为Feign默认提供的配置类。
userUserFeignClient登录账号user,adminUserFeignClient登录账号admin,它们使用的是同一个接口UserFeignClient。
测试
1)启动microservice-discovery-eureka。
2)启动microservice-provider-user-with-auth。
3)启动microservice-consumer-movie-feign-manual。
4)访问http://localhost:8010/user-user/1,可以正常获得查询结果。
5)访问http://localhost:8010/user-admin/1,可以正常获得查询结果。
Feign对继承的支持
Feign支持继承。使用继承,可将一些公共操作分组到一些父接口中,从而简化Feign的开发。
- 基础接口:UserService.java
public interface UserService {
@RequestMapping(value = "/users/{id}",method = RequestMethod.GET)
public User getUser(@PathVariable("id") Long id);
}
- 服务提供者Controller:UserResource.java
@RestController
public class UserController implements UserService{
......
}
- 服务消费者:UserClient.java
@FeignClient(name = "name")
public interface UserFeignClient {
}
注:尽管Feign的继承可帮助我们进一步简化Feign的开发,但Spring Cloud官方指出——通常情况下,不建议在服务器端与客户端之间共享接口,因为这种方式造成了服务器端与客户端代码的紧耦合。并且,Feign本身并不使用Spring MVC的工作机制(方法参数映射不被继承)。
Feign对压缩的支持
在一些场景下,可能需要对请求或响应进行压缩,此时可使用以下属性启用Feign的压缩功能。
feign:
compression:
request:
enable: true
mime-types: text/xml,application/xml,application/json
min-request-size: 2048
response:
enable: true
其中,feign.compression.request.mime-types用于支持的媒体类型列表,默认是text/xml、application/xml以及application/json。
feign.compression.request.min-request-size用于设置请求的最小阈值,默认是2048。
Feign的日志
Feign对日志的处理非常灵活,可为每个Feign客户端指定日志记录策略,每个Feign客户端都会创建一个logger。默认情况下,logger的名称是Feign接口的完整类名。需要注意的是,Feign的日志打印只对对DEBUG级别做出响应。
我们可为每个Feign客户端各自的Logger.Level对象,告诉Feign记录哪些日志。Logger-Level的值有以下选择。
- NONE:不记录任何日志(默认值)。
- BASIC:仅记录请求方法、URL、响应状态代码以及执行时间。
- HEADERS:记录BASIC级别的基础上,记录请求和响应的header。
- FULL:记录请求和响应的header、body和元数据。
编码方式设置日志级别
下面来为前面编写的UserFeignClient添加日志打印,将它的日志级别设置为FULL。
1)复制项目microservice-consumer-movie-feign,将ArtifactId修改为microservice-consumer-movie-feign-logging。
2)编写Feign配置类
package com.example.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignLogConfiguration {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
3)修改Feign的接口,指定配置类:
package com.example.feign;
import com.example.config.FeignLogConfiguration;
import com.example.entity.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.cloud.netflix.feign.FeignClientsConfiguration;
import org.springframework.cloud.netflix.feign.FeignLoggerFactory;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient(name = "microservice-provider-user",configuration = FeignLogConfiguration.class)
public interface UserFeignClient {
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
public User findById(@PathVariable("id") Long id);
}
4)在application.yml中添加以下内容,指定Feign接口的日志级别为DEBUG
logging:
level: #将Feign接口的日志级别设置为DEBUG,因为Feign的Logger.Level只对DEBUG做出响应。
com.example.feign.UserFeignClient: DEBUG
测试
1)启动microservice-discovery-eureka。
2)启动microservice-provider-user。
3)启动microserivce-consumer-movie-feign-logging。
4)访问http://localhost:8010/user/1,可以看到日志。
可以看到,Feign已将请求的各种细节非常详细地记录了下来。
5)将日志Feign的日志级别改为BASIC,重启项目后再次访问http://localhost:8010/user/1,会发现此时只会打印请求方法、请求的URL、响应的状态码和响应的时间等信息。
使用属性配置日志级别
从Spring Cloud Edgware开始,也可使用配置属性直接定义Feign的日志级别
logging:
level:
com.example.feign.UserFeignClient: DEBUG
feign:
client:
config:
microservice-provider-user:
loggerLevel: full
使用Feign构造多参数请求
GET请求多参数的URL
假设请求的URL包括多个参数,要如何构造呢?
我们知道,Spring Cloud为Feign添加了Spring MVC的注解支持,那么不妨按照Spring MVC的写法尝试一下
@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
@RequestMapping(value = "/get",method = RequestMethod.GET)
public User get(User user);
}
然而这种写法并不正确,控制台会输出类似如下的异常。
由异常可知,尽管指定了GET方法,Feign依然会使用POST方法发送请求。
正确的写法如下
方法一
@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
@RequestMapping(value = "/get",method = RequestMethod.GET)
public String get(@RequestParam("id") Long id,@RequestParam("username") String username);
}
这是最为直观的方式,URL有几个参数,Feign接口中的方式就有几个参数。使用@RequestParam注解指定具体的请求参数。
方法二:
多参数的URL也可使用Map来构建。当目标URL参数非常多时,可使用这种方式简化Feign接口的编写。
@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
@RequestMapping(value = "/get",method = RequestMethod.GET)
public String get(@RequestParam Map<String,Object>map);
}
POST请求包含多个参数
使用Feign构造包含多个参数的POST请求。假设服务提供者的Controller是这样编写的:
@PostMapping("/post")
public User post(@RequestBody User user){
return user;
}
使用Feign去请求
@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
@RequestMapping(value = "/post",method = RequestMethod.POST)
public String get(@RequestBody User user);
}
@GetMapping("/post")
public String post(){
User user=new User();
user.setId(1L);
user.setUsername("张三");
return this.userFeignClient.get(user);
}