spring boot:Feign调用远程HTTP服务

2019-11-03  本文已影响0人  远方的橄榄树
Content is better than riches

1. 简介

  1. 调用远程服务器的HTTP接口时,通常有以下几种方式:
  1. spring cloud feign是声明性Web服务客户端。Feign客户端使用@FeignClient注册组合成组件,可以实现调用远程服务器。Feign通过接口的方式发起HTTP请求,这种方法相对简便。

2. 基本使用

1.创建maven项目

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
          https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.samllbear</groupId>
    <artifactId>feign-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>feign-demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--添加Feign依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--集成org.apache.http-components, 使用httpclient作为client。
        默认情况下,feign通过jdk中的HttpURLConnection向下游服务发起http请求,
        这种方式缺乏连接池的支持,所以我们选用http-client-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
            <version>10.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
  1. 修改配置文件
feign:
 httpclient:
   enabled: true #开启httpClient
 hystrix:
   enabled: false #没有用到

server:
 port: 8081
 servlet:
   context-path: /feign

logging:
 level:
   com:
     samllbear:
       feigindemo:
         debug  #开启DEBUG日志

user-url: http://localhost:${server.port}${server.servlet.context-path}

3、在启动类上中添加注解@EnableFeignClients

  1. 举例应用,我们通过TestController来实现对UserController接口的调用。
/**
*实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class User {

    private Long id;

    private String name;

    private String nickname;

    private Integer age;
}
/**
*DTO模板类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class BaseDto<T> {

    private Integer code;

    private String msg;

    private T data;
}
import feign.codec.Encoder;
import feign.form.FormEncoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;

@Configuration
public class FeignConfig {

    @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters;

    //Feign表单提交配置
    @Bean
    @Primary
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    Encoder formEncoder() {
        return new FormEncoder(new SpringEncoder(this.messageConverters));
    }
}
/**
 * 通过Feign调用UserController的api
 */
@FeignClient(value = "userClient", url = "${user-url}/user")
public interface UserClient {

    @GetMapping("get/{id}")
    BaseDto<User> get(@PathVariable("id") Long id);

    @GetMapping("list")
    BaseDto<List<User>> list();

    //提交表单
    //头部(header)默认是Content-Type: application/json
    //我们将其设置成application/x-www-form-urlencoded便可提交表单对象(存放在body中)
    @PostMapping(value = "add", consumes =
            MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    BaseDto<User> add(@RequestBody User user);

}
/**
 *创建一个service, 模拟数据库操作
 */
@Service
public class UserService {

    private static List<User> users = new ArrayList<>();

    static {
        users.add(new User(1L, "亚索", "托儿所", 21));
        users.add(new User(2L, "盲僧", "小学僧", 23));
        users.add(new User(3L, "阿狸", null, 26));
        users.add(new User(4L, "阿卡丽", null, 21));
    }

    public User get(Long id) {
        Optional<User> userOpt = users.stream()
                .filter(user -> user.getId().equals(id))
                .findFirst();
        return userOpt.isPresent() ? userOpt.get() : null;
    }

    public List<User> list() {
        return users;
    }

    public User insert(User user) {
        user.setId(users.size() + 1L);
        users.add(user);
        return user;
    }
}

@RestController
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("get/{id}")
    public BaseDto<User> get(@PathVariable("id") Long id) {
        User user = userService.get(id);
        return new BaseDto<>(200, "访问成功", user);
    }

    @GetMapping("list")
    public  BaseDto<List<User>> list() {
        List<User> users = userService.list();
        return new BaseDto<>(200, "访问成功", users);
    }

    @PostMapping("add")
    public BaseDto<User> add(User user) {
        User newUser = userService.insert(user);
        return new BaseDto<>(200, "访问成功", newUser);
    }
}
/**
*测试类
*/
@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    private UserClient userClient;

    @GetMapping("testGet/{id}")
    public BaseDto<User> testGet(@PathVariable("id") Long id) {
        return userClient.get(id);
    }

    @GetMapping("/testList")
    public BaseDto<List<User>> testList() {
        return userClient.list();
    }

    @PostMapping("/testAdd")
    public BaseDto<User> testAdd(User user) {
        return userClient.add(user);
    }
}
  1. 测试结果
    a. 启动工程,访问http://127.0.0.1:8081/feign/test/testGet/1,浏览器显示
{
    "code": 200,
    "msg": "访问成功",
    "data": {
        "id": 1,
        "name": "亚索",
        "nickname": "托儿所",
        "age": 21
    }
}

b. 启动工程,访问http://127.0.0.1:8081/feign/test/testList,浏览器显示

{
    "code": 200,
    "msg": "访问成功",
    "data": [
        {
            "id": 1,
            "name": "亚索",
            "nickname": "托儿所",
            "age": 21
        },
        {
            "id": 2,
            "name": "盲僧",
            "nickname": "小学僧",
            "age": 23
        },
        {
            "id": 3,
            "name": "阿狸",
            "nickname": null,
            "age": 26
        },
        {
            "id": 4,
            "name": "阿卡丽",
            "nickname": null,
            "age": 21
        }
    ]
}

c. 启动工程,访问http://127.0.0.1:8081/feign/test/testAdd,结果如下图所示:

实例

3. 设置请求头部信息

  1. 方法一:利用@RequestMapping设置header

UserClient.java中添加

//    @Headers({"Content-Type=application/json", "greeting=hello, world"}) 
//    Feign提供的注解@Header不起作用,不知道为啥
    @GetMapping(value = "header", 
        headers = {"Content-Type=application/json", "greeting=hello, world"})
    User header();

UserController.java添加

    @GetMapping("header")
    public User header(HttpServletRequest request) {
        System.out.println(request.getHeader("greeting")); // hello, World
        System.out.println(request.getHeader("Content-Type")); // application/json
        return new User(6L, "劫", "儿童劫", 43);
    }

TestController.java中添加

    @GetMapping("testHeader")
    public User testHeader() {
        return userClient.header();
    }

启动工程,访问http://127.0.0.1:8081/feign/test/testHeader,结果如下:{ "id": 6, "name": "劫", "nickname": "儿童劫", "age": 43 },并且控制台打印hello, Worldapplication/json

  1. 方法二:实现RequestInterceptor接口设置header信息
    FeignConfig.java修改如下
/**
*Feign发起请求的拦截器
*/
public class FeignInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        requestTemplate.header("Greeting", "hello, World")
                .header("TimeStamp", Long.toString(System.currentTimeMillis()));
    }
}
import feign.Logger;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;

@Configuration
public class FeignConfig {

    @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters;

    //Feign表单提交配置
    @Bean
    @Primary
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    public Encoder feignFormEncoder() {
        return new SpringFormEncoder(new SpringEncoder(messageConverters));

    //feign拦截器,设置header信息
    @Bean
    FeignInterceptor feignInterceptor() {
        return new FeignInterceptor();
    }

    //开启feign日志
    @Bean
    Logger.Level feignLevel() {
        return Logger.Level.FULL;
    }
}

修改UserClient.java@FeignClient注解

//设置配置文件
@FeignClient(value = "userClient", url = "${user-url}/user", 
      configuration = FeignConfig.class)
上一篇 下一篇

猜你喜欢

热点阅读