使用Feign实现声明式REST调用

使用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接口使用不同的账号登录,并调用用户微服务的接口。

修改用户微服务

  1. 复制项目microservice-provider-user,将ArtufactId修改为microservice-provider-user-with-auth。
  2. 为项目添加以下依赖
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
  1. 创建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

  1. 修改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);
    }

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值