Skip to content

Call Rest Api by FeignClient

🏷️ Spring Cloud Feign

之前写过一篇使用Feign.builder()来发送 HTTP 请求的博客,对应的接口注解使用的是@RequestLine而不是常用的@RequestMapping

@RequestLinefeign-core 包提供的注解,@RequestMapping@PostMapping@GetMapping等是 spring-web 包提供的注解,两个使用方法不大一样。

1. 添加依赖

添加 spring-cloud-starter-openfeignfeign-httpclient 依赖。
其中 feign-httpclient 是为了将默认的客户端feign.Client.Default替换为HttpClient

xml
<!-- Feign -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>2.2.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
    <version>11.6</version>
</dependency>

2. 启用 HttpClient

application.ymlbootstrap.yml 中添加如下配置以启用 HttpClient

yaml
feign:
  httpclient:
    enabled: true

3. 添加 FeignClient 服务

代码示例如下。

其中@FeignClientname 属性指定了客户端的名称(后面配置打印日志时会用到),可以在配置文件中通过这个属性对特定的服务指定配置。

另外 url 属性中使用 SpEL(Spring Expression Language) 以指定默认值的同时,还可以通过配置文件来修改这个值(修改后需要重启服务才会生效)。

这里是 GET 方法的示例,这种写法会导致参数太多,但又没有查到比较好的使用对象来传递 GET 参数的方法,建议参数过多时还是使用 POST 方法。

java
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * 用户数据服务
 *
 * @author jiajia
 */
@FeignClient(name = "userDataService", url = "${user-data-server.domain:" + AdDataConstants.AD_DATA_DOMAIN + "}")
public interface UserDataService {

    /**
     * 推送用户数据
     *
     * @param uid
     * @param appId
     * @param imei
     * @param oaid
     * @param mac
     * @param androidId
     * @param ip
     * @param ua
     * @param createTime
     * @return
     */
    @GetMapping(value = "api/app/user", consumes = MediaType.APPLICATION_JSON_VALUE)
    UserDataPublicResponse pushUserData(
            @RequestParam("id") String id,
            @RequestParam("app_id") String appId,
            @RequestParam("imei") String imei,
            @RequestParam("oaid") String oaid,
            @RequestParam("mac") String mac,
            @RequestParam("android_id") String androidId,
            @RequestParam("ip") String ip,
            @RequestParam("ua") String ua,
            @RequestParam("create_time") String createTime
    );
}

4. 启用 FeignClient

通过@EnableFeignClientsbasePackages 属性指定需要扫描的 FeignClient 所在的包名。
可以像下面的示例一样,通过单独的类来配置,也可以在启动类上添加这个注解来配置。

java
/**
 * 用户数据服务配置
 *
 * @author jiajia
 */
@Component
@EnableFeignClients(basePackages = { "me.liujiajia.user.service" })
public class UserDataServiceConfiguration {

}

5. 测试

如果需要自动化测试的,可以参考如下代码。

首先要添加 spring-boot-starter-test 依赖。

xml
<!-- Test -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>2.2.13.RELEASE</version>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>

添加测试类,通过@SpringBootTest已加载各种配置,通过@Test注解标记测试方法。关于自动化测试的 Mock 可以参考之前的一篇博客

java
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@Slf4j
@SpringBootTest
public class UserDataServiceTest {

    @Autowired
    private UserDataService userDataService;

    @Test
    public void should_success_when_push_user_data() {
        UserDataPublicResponse response = userDataService.pushUserData(
                "1",
                "a_app_id",
                "869659051398480",
                "97e7ff3f-e5f3-d1b8-ccfc-f79bbeaf4841",
                "20:00:00:00:00:00",
                "873541edf36da917",
                "127.0.0.1",
                "Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 SP-engine/2.30.0 main%2F1.0 baiduboxapp/12.14.5.10 (Baidu; P2 14.6) NABar/1.0 themeUA=Theme/default",
                "2021-10-20 12:00:00"
        );

        Assertions.assertNotNull(response);
        Assertions.assertEquals(response.getCode(), 0);
        Assertions.assertEquals(response.getMessage(), "ok");
    }
}

6. 打印请求的日志

默认是不打印 HTTP 请求的具体信息的,如需要可以通过添加如下配置来启用。

  • userDataService@FeignClient注解中 name 属性指定的客户端名称;
  • logger-level 的值见feign.Logger.Level枚举,可选值有 NONEBASICHEADERSFULL
  • logging.level 中指定的是Client类的包名,日志级别的常用值值有 ERRORWARNINFODEBUGTRACE
yaml
feign:
  client:
    config:
      userDataService:
        logger-level: full

logging:
  level:
    com.client.client: DEBUG