13.原始功能案例使用篇

一、前言

前面已经介绍完了feign的整个原生框架的代码, 本节将给大家带来原生feign框架的使用细节

二、案例

约定

1.先定义一个数据载体

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {

    private String name;

    private Integer age;

    private Integer gender;

    private LocalDate birthday;
}

2.引入编解码器和okhttp

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
    <version>13.3</version>
</dependency>

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-jackson</artifactId>
    <version>13.3</version>
</dependency>

1.@Param注解

// controller
@PostMapping("/paramTest/path")
public Person paramTest(@RequestParam("name") String name, @RequestBody Person person,
                       @RequestHeader Map<String, Object> headers) {
    System.out.println("uncleqiao name:" + name);
    System.out.println("uncleqiao 收到body:" + person);
    person.setName("小杜同学");
    person.setGender(0);
    return person;
}

// feign接口
@RequestLine("POST /paramTest/{path}?name={name}")
@Headers({"Content-Type:application/json", "Authorization:{auth}"})
Person paramTest(@Param("path") String path, @Param String name, @Param("auth") String authorization, @Param Integer age, @Param LocalDate birthday);

// 测试类
@Test
void paramTest() {
    com.fasterxml.jackson.databind.Module javaTimeModule = new JavaTimeModule();
    List<Module> modules = List.of(javaTimeModule);

    FeignDemoInterface feignDemoInterface = Feign
            .builder()
            .encoder(new JacksonEncoder(modules))
            .decoder(new JacksonDecoder(modules))
            .target(FeignDemoInterface.class, "http://localhost:8080");
    Person person = feignDemoInterface.paramTest("path", "uncleqiao", "abc123", 18, LocalDate.now());
    System.out.println(person);
}

// 服务端打印
uncleqiao name:uncleqiao
uncleqiao 收到body:Person(name=null, age=18, gender=null, birthday=2024-12-04)
请求头来了:{authorization=abc123, content-type=application/json, accept=*/*, user-agent=Java/17.0.7, host=localhost:8080, connection=keep-alive, content-length=48}


// 客户端打印
Person(name=小杜同学, age=18, gender=0, birthday=2024-12-04)

小结

  1. 测试了@Param参数替换占位符的三个场景,
  • /paramTest/{path}, 请求行上
  • url参数上name={name}, 会被@RequestParam注解接收
  • 请求头上Authorization:{auth}, 会被@RequestHeader注解接收到
  1. @Param Integer age, @Param LocalDate birthday这两个参数由于不会替换占位符, 会被JacksonEncoder编码器写入到body中(请求体), 可以被@RequestBody注解接收到

2.使用Expander定制param

// controller
@PostMapping("paramExpanderTest")
public void paramExpanderTest(@RequestBody Person person) {
    System.out.println("person:" + person);
}

// 自定义一个Expander
public class ExpanderTest implements Param.Expander  {

    @Override
    public String expand(Object value) {
        System.out.println("原值为:" + value);
        return "小乔同学";
    }
}

// feign接口
@RequestLine("POST /paramExpanderTest")
@Headers("Content-Type:application/json")
Person paramExpanderTest(@Param(expander = ExpanderTest.class) String name);

// 测试
@Test
void paramExpanderTest() {
    com.fasterxml.jackson.databind.Module javaTimeModule = new JavaTimeModule();
    List<Module> modules = List.of(javaTimeModule);
    FeignDemoInterface feignDemoInterface = Feign
            .builder()
            .encoder(new JacksonEncoder(modules))
            .decoder(new JacksonDecoder(modules))
            .target(FeignDemoInterface.class, "http://localhost:8080");
    feignDemoInterface.paramExpanderTest("小杜同学");
}

// 结果
// controller
person:Person(name=小乔同学, age=null, gender=null, birthday=null)
    
// ExpanderTest
原值为:小杜同学

注意: 要是即设置了请求拦截器也设置了Expander来修改某个值, 那么拦截器的优先级会更高, 因为是先解析参数再执行的拦截器

3.@Headers与@HeaderMap

headers注解可以添加到接口、方法上, HeaderMap注解可以用来修饰参数, 都是用来添加请求头参数的

// controller
@GetMapping("/headerTest")
public void header(@RequestHeader HttpHeaders headers) {
    System.out.println("headerMap:" + headers);
}

// feign接口
@Headers({"Content-Type:application/json", "customHeader:class"})
public interface FeignDemoInterface {
    @RequestLine("GET /headerTest")
    @Headers({"accept:application/json", "customHeader:method"})
    void headerTest(@HeaderMap Map<String, Object> headerMap);
}

// 测试
@Test
void headerTest() {
    com.fasterxml.jackson.databind.Module javaTimeModule = new JavaTimeModule();
    List<Module> modules = List.of(javaTimeModule);

    FeignDemoInterface feignDemoInterface = Feign
            .builder()
            .encoder(new JacksonEncoder(modules))
            .decoder(new JacksonDecoder(modules))
            .target(FeignDemoInterface.class, "http://localhost:8080");
    Map<String, Object> heardMap = Map.of("Authorization", "123mtr", "customHeader", "param");
    feignDemoInterface.headerTest(heardMap);
}

// 服务端打印
headerMap:[accept:"application/json", authorization:"123mtr", content-type:"application/json", customheader:"class, method", "param", user-agent:"Java/17.0.7", host:"localhost:8080", connection:"keep-alive"]

小结

  1. 这里测试了@Headers放在接口, 方法上, 并且@HeaderMap放在参数上的场景
  2. 细心的朋友肯定发现了我这里用的@RequestHeader HttpHeaders接收请求头而不是用@RequestHeader Map<String, Object> headers的形式接收; 我们看到**customheader:“class, method”, “param”**是返回了两个值, 而不是一个或者三个
  3. 当使用HttpHeaders时, 也可以使用headers.getValuesAsList("customHeader")获取请求头, 返回的将是平铺的list
  4. 原因是feign在添加请求头时, class和method的请求头合并在一起, 而参数中的作为它们下级元素组装在一起; 使用@HeaderMap时, 只能获取最简单的key,value格式, 嵌套的处理不了, 所以这里推荐大家使用@RequestHeader HttpHeaders headers方法获取请求头

注意: 默认情况下, @Param参数是用来替换占位符的, 如果某个@Param参数不能用来替换占位符, 它们会被组装成map, 但是默认的编码器只能对string和byte[]进行编码, 所以这种情况将会报错, 当设置了可以解析map的编码器, 例如jackson、fastjson等, 就会将此时的map编码后设置成请求体body的参数进行传递

4.URI参数

使用uri参数可以设置请求的相对路径

// controller
@GetMapping("/uriTest/relativePath")
public void uriTest(@RequestParam("name") String name, @RequestParam("age") Integer age, @RequestParam("birthday") String birthday) {
    System.out.println("name:" + name);
    System.out.println("age:" + age);
    System.out.println("birthday:" + birthday);
}

// feign接口
@RequestLine("GET /relativePath?name=uncleqiao")
void uriTest(URI uri);

// 测试
@Test
void uriTest() {
    FeignDemoInterface feignDemoInterface = Feign
            .builder()
            .target(FeignDemoInterface.class, "http://localhost:8080");
    feignDemoInterface.uriTest(URI.create("http://localhost:8080/uriTest?age=18&birthday=" + LocalDate.now()));
}

// 服务端打印
name:uncleqiao
age:18
birthday:2024-12-04

参数解析顺序如下

  1. RequestLine 取了请求行路径 -> uriTemplate

  2. uri -> 取值: target , fragment, 追加参数

  3. feign.target -> 取值: target , fragment, 追加参数

  4. 最终请求地址: target + uri + 参数 + fragment

注意: 如果设置了URI参数, 那么feign.target设置的路径将会失效

5.@QueryMap

1.使用map做参数

// controller借用uriTest中的

// feign接口
@RequestLine("GET /uriTest/relativePath")
void queryMapTest(@QueryMap Map<String, Object> param);

// 测试
@Test
void queryMapTest() {
    FeignDemoInterface feignDemoInterface = Feign
            .builder()
            .target(FeignDemoInterface.class, "http://localhost:8080");
    Map<String, Object> heardMap = Map.of("name", "uncleqiao", "age", 18, "birthday", "2024-08-08");
    feignDemoInterface.queryMapTest(heardMap);
}

// 结果
// 服务端打印
name:uncleqiao
age:18
birthday:2024-08-08

说明: @QueryMap注解标识的如果是map参数, 那么key只能是字符串格式

2.使用javaBean做参数

// feign接口
@RequestLine("GET /uriTest/relativePath")
void queryMapTest2(@QueryMap Person person);

// 测试
@Test
void queryMapTest2() {
    FeignDemoInterface feignDemoInterface = Feign
            .builder()
            .target(FeignDemoInterface.class, "http://localhost:8080");
    Person person = new Person("uncleqiao", 18, 1, LocalDate.now());
    feignDemoInterface.queryMapTest2(person);
}

说明:

  1. @QueryMap注解标识的如果是对象参数, 那么对象中所有满足javaBean规则的get方法都会生成一个参数添加到请求上
  2. 默认使用的是FieldQueryMapEncoder解析javaBean对象

6.上传文件

// controlelr
@PostMapping("/upload")
public void uploadTest(@RequestBody byte[] file, @RequestParam("filename") String filename) {
    System.out.println("filename:" + filename);
    System.out.println("file:" + file);

    // 目标文件路径
//        String filePath = "output.txt";

    // 将 byte[] 数据写入文件
//        try (FileOutputStream fos = new FileOutputStream(filePath)) {
//            fos.write(file);
//            System.out.println("File saved successfully at: " + filePath);
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
}

// feign接口
@RequestLine("POST /upload?filename={filename}")
void uploadTest(byte[] file, @Param String filename);

// 测试
@Test
void uploadFileTest() throws Exception {
    FeignDemoInterface feignDemoInterface = Feign
            .builder()
            .target(FeignDemoInterface.class, "http://localhost:8080");
    FileInputStream fileInputStream = new FileInputStream("/Users/uncleqiao/Documents/upload_file.txt");
    byte[] bodyData = Util.toByteArray(fileInputStream);
    feignDemoInterface.uploadTest(bodyData, "upload_file.txt");
    fileInputStream.close();
}

// 结果
// 服务端打印
filename:upload_file.txt
file:[B@3eb6e6ef

7.异步请求

// controlelr
@GetMapping("/asyncTest")
public Person asyncTest(@RequestParam("name") String name, @RequestParam("age") Integer age) {
    System.out.println("name:" + name);
    System.out.println("age:" + age);
    Person person = new Person();
    person.setName("小杜同学");
    person.setAge(16);
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
    }
    return person;
}

// feign接口
@RequestLine("GET /asyncTest")
CompletableFuture<Person> asyncTest(@QueryMap Person person);

// 测试
@Test
void asyncTest() throws Exception {
    FeignDemoInterface feignDemoInterface = AsyncFeign
            .builder()
            .decoder(new JacksonDecoder())
            .target(FeignDemoInterface.class, "http://localhost:8080");
    Person person = new Person("小乔同学", 18, 1, LocalDate.now());
    CompletableFuture<Person> personCompletableFuture = feignDemoInterface.asyncTest(person);
    System.out.println("当前时间:" + LocalDateTime.now());
    Person person1 = personCompletableFuture.get();
    System.out.println("当前时间:" + LocalDateTime.now());
    System.out.println(person1);
}

// 结果
// 服务端打印
name:小乔同学
age:18

// 客户端打印
当前时间:2024-12-04T22:27:07.219589
当前时间:2024-12-04T22:27:12.272539
Person(name=小杜同学, age=16, gender=null, birthday=null)

到此, 所有的有关feign的原生功能全部介绍完毕.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

uncleqiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值