02 Feign基本注解介绍


大家接触Feign都是通过Spring Cloud,平时使用的均是Spring MVC的注解。但是Feign是完全不依赖SpringMvc的注解的,是可以直接使用的哟。

1 @RequestLine

1.1 介绍

// 只能用在方法上
@java.lang.annotation.Target(METHOD) 
@Retention(RUNTIME)
public @interface RequestLine {
  //定义请求方式,请求路径,还有请求参数
  String value();
  // 是否编码/符号,默认是会编码的,也就是转义的意思
  boolean decodeSlash() default true;
	// 默认支持URL传多值,是通过key来传输的。形如:key=value1&key=value2&key=value3
  // CollectionFormat不同的取值对应不同的分隔符,一般不建议改
  CollectionFormat collectionFormat() default CollectionFormat.EXPLODED;
}
  • 只能用在方法上
  • 通过value参数定义请求方式,请求路径,还有请求参数

1.2 演示

1.2.1 准备工作:Feign日志打印

Feign内置了日志打印的

  • feign.Logger.JavaLogger:使用的java.util.logging.Logger输出,但是日志级别的FINE级别,默认不会输出到控制台
  • feign.Logger.ErrorLogger:错误输出。使用的System.err.printf()输出
  • feign.Logger.NoOpLogger:什么都不输出,它是Feign的默认使用的Logger实现,也就是不会给控制台输出

定义一个Feign客户端构建器:

package study.wyy.feign.java.spi;
import feign.Feign;
import feign.Logger;
import feign.Retryer;

/**
 * @author by wyaoyao
 * @Description
 * @Date 2020/12/16 4:21 下午
 */
public class FeignClientBuilder<T> {

    public static String HOST = "http://localhost:8001";
    public static <T> T build(Class<T> clazz){
       return Feign.builder()
                // 输出日志到控制台
                .logger(new Logger.ErrorLogger()).logLevel(Logger.Level.FULL)
                // 关闭重试
                .retryer(Retryer.NEVER_RETRY)
                // 404进行编码,404的时候就不会抛出异常了
                .decode404()
                .target(clazz,HOST);
    }
}
1.2.2 服务提供

00-service-provider模块提供下面两个rest接口

package study.wyy.feign.provider.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**
 * @author by wyaoyao
 * @Description
 * @Date 2020/12/16 5:17 下午
 */
@Slf4j
@RequestMapping("/feign/provider")
@RestController
public class RequestLineController {

    /****
     * 测试基本使用:和入门案例一样
     */
    @GetMapping("/requestLine1")
    public String RequestLine1(@RequestParam String name){
        return "RequestLine " + name;
    }

    /****
     * 测试参数为map
     */
    @GetMapping("/requestLine2")
    public String RequestLine2(@RequestParam Map<String,Object> param) throws JsonProcessingException {
        log.info("requestLine2 param: {}",param);
        // 转成字符串返回
        return new ObjectMapper().writer().writeValueAsString(param);
    }

    /****
     * 测试传递多个参数
     */
    @GetMapping("/requestLine3")
    public String RequestLine3(@RequestParam String name,@RequestParam int num){
        return "RequestLine " + name + " " + num;
    }
}

1.2.3 Feign客户端
  1. 声明对应的接口
package study.wyy.feign.java.consumer;

import feign.Param;
import feign.QueryMap;
import feign.RequestLine;

import java.util.Map;

/**
 * @author: wyaoyao
 * @date: 2020-12-17 14:04
 * @description: requestLine注解演示
 */
public interface RequestLineClient {

    /****
     * 测试基本使用:和入门案例一样
     */
    @RequestLine("GET /feign/provider/requestLine1?name={name}")
    String consumerRequestLine1(@Param("name") String name);

    /****
     * 测试参数为map
     */
    @RequestLine("GET /feign/provider/requestLine2")
    String consumerRequestLine2(@QueryMap Map<String,Object> param);

    /****
     * 测试基本使用:和入门案例一样
     */
    @RequestLine("GET /feign/provider/requestLine3?name={name}&num={num}")
    String consumerRequestLine3(@Param("name") String name,@Param("num") int num);
}
  1. 测试
package study.wyy.feign.java.test;

import org.junit.Before;
import org.junit.Test;
import study.wyy.feign.java.consumer.RequestLineClient;
import study.wyy.feign.java.spi.FeignClientBuilder;

import java.util.HashMap;
import java.util.Map;

/**
 * @author: wyaoyao
 * @date: 2020-12-17 14:00
 * @description: @RequestLine 测试
 */
public class RequestLineTest {
    private RequestLineClient client;
    @Before
    public void before(){
        client  = FeignClientBuilder.build(RequestLineClient.class);
    }
    @Test
    public void testConsumerRequestLine1() {
        String result = client.consumerRequestLine1("Feign");
        System.out.println(result);
    }

    @Test
    public void testConsumerRequestLine2() {
        Map<String, Object> param = new HashMap<>();
        param.put("name","Feign");
        param.put("num",2020);
        String s = client.consumerRequestLine2(param);
        System.out.println(s);
    }

    @Test
    public void testConsumerRequestLine3() {
        String s = client.consumerRequestLine3("Feign",2020);
        System.out.println(s);
    }
}

输出:

[RequestLineClient#consumerRequestLine1] ---> GET http://localhost:8001/feign/provider/requestLine1?name=Feign HTTP/1.1
[RequestLineClient#consumerRequestLine1] ---> END HTTP (0-byte body)
[RequestLineClient#consumerRequestLine1] <--- HTTP/1.1 200 (104ms)
[RequestLineClient#consumerRequestLine1] connection: keep-alive
[RequestLineClient#consumerRequestLine1] content-length: 17
[RequestLineClient#consumerRequestLine1] content-type: text/plain;charset=UTF-8
[RequestLineClient#consumerRequestLine1] date: Thu, 17 Dec 2020 07:05:20 GMT
[RequestLineClient#consumerRequestLine1] keep-alive: timeout=60
[RequestLineClient#consumerRequestLine1] 
[RequestLineClient#consumerRequestLine1] RequestLine Feign
[RequestLineClient#consumerRequestLine1] <--- END HTTP (17-byte body)
RequestLine Feign
[RequestLineClient#consumerRequestLine2] ---> GET http://localhost:8001/feign/provider/requestLine2?num=2020&name=Feign HTTP/1.1
[RequestLineClient#consumerRequestLine2] ---> END HTTP (0-byte body)
[RequestLineClient#consumerRequestLine2] <--- HTTP/1.1 200 (20ms)
[RequestLineClient#consumerRequestLine2] connection: keep-alive
[RequestLineClient#consumerRequestLine2] content-length: 29
[RequestLineClient#consumerRequestLine2] content-type: text/plain;charset=UTF-8
[RequestLineClient#consumerRequestLine2] date: Thu, 17 Dec 2020 07:05:20 GMT
[RequestLineClient#consumerRequestLine2] keep-alive: timeout=60
[RequestLineClient#consumerRequestLine2] 
[RequestLineClient#consumerRequestLine2] {"num":"2020","name":"Feign"}
[RequestLineClient#consumerRequestLine2] <--- END HTTP (29-byte body)
{"num":"2020","name":"Feign"}
[RequestLineClient#consumerRequestLine3] ---> GET http://localhost:8001/feign/provider/requestLine3?name=Feign&num=2020 HTTP/1.1
[RequestLineClient#consumerRequestLine3] ---> END HTTP (0-byte body)
[RequestLineClient#consumerRequestLine3] <--- HTTP/1.1 200 (3ms)
[RequestLineClient#consumerRequestLine3] connection: keep-alive
[RequestLineClient#consumerRequestLine3] content-length: 22
[RequestLineClient#consumerRequestLine3] content-type: text/plain;charset=UTF-8
[RequestLineClient#consumerRequestLine3] date: Thu, 17 Dec 2020 07:05:20 GMT
[RequestLineClient#consumerRequestLine3] keep-alive: timeout=60
[RequestLineClient#consumerRequestLine3] 
[RequestLineClient#consumerRequestLine3] RequestLine Feign 2020
[RequestLineClient#consumerRequestLine3] <--- END HTTP (22-byte body)
RequestLine Feign 2020

1.3 使用注意点

  1. 请求方法必须大写:GET,POST。。
    因为底层是通过下面的枚举feign.Request.HttpMethod映射
  public enum HttpMethod {
    GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH
  }
  1. RequestLine注解的首个单词必须是HTTP方法,且必须顶格写(前面不允许有空格),但后面是需要有空格

2 @Param

2.1 介绍

通过名称定义模板变量,其值将用于填入上面的模版:@Headers/@RequestLine/@Body均可使用模版表达式。
比如说刚刚的:

/****
 * RequestLine 就是定义一个模板,定义了一下内容:
 *  请求方式
 *  请求路径
 *  请求参数,@Param就会把形参中的具体填充到模板中的name参数
 */
@RequestLine("GET /feign/provider/requestLine1?name={name}")
String consumerRequestLine1(@Param("name") String name);
@Retention(RUNTIME)
// 只能用在参数上
@java.lang.annotation.Target(PARAMETER)
public @interface Param {
  /**
   * 名称(key),和模版会进行匹配然后填充 必填项
   */
  String value();
  /**
   * 如何把值填充上去,默认是调用其toString方法直接填上去
   */
  Class<? extends Expander> expander() default ToStringExpander.class;
  /**
   * 是否转义,默认不转义,直接放上去
   */
  boolean encoded() default false;

  interface Expander {
    /**
     * Expands the value into a string. Does not accept or return null.
     */
    String expand(Object value);
  }
  final class ToStringExpander implements Expander {
    @Override
    public String expand(Object value) {
      return value.toString();
    }
  }
}

2.2 使用演示

2.2.1 基本使用演示
  1. 定义一个rest接口
package study.wyy.feign.provider.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;


/**
 * @author by wyaoyao
 * @Description
 * @Date 2020/12/16 5:17 下午
 */
@Slf4j
@RequestMapping("/feign/provider")
@RestController
public class ParamController {
    @GetMapping("/param1")
    public String param1(@RequestParam String name){
        return "@Param " + name;
    }
}
  1. 定义Feign接口
package study.wyy.feign.java.consumer;

import feign.Param;
import feign.RequestLine;

/**
 * @author: wyaoyao
 * @date: 2020-12-17 15:45
 * @description:
 */
public interface ParamClient {

    /**
     * 测试1: 使用Param注解
     * @param
     * @return
     */
    @RequestLine("GET /feign/provider/param1?name={name}")
    String consumerParam1(@Param("name") String str);
    
    /**
     * 测试1: 不使用Param注解,形参和模板中的参数key对应
     * @param
     * @return
     */
    @RequestLine("GET /feign/provider/param1?name={name}")
    String consumerParam2(String name);
    
    /**
     * 测试1: 不使用Param注解,但是形参和模板中的参数key不对应
     * @param
     * @return
     */
    @RequestLine("GET /feign/provider/param1?name={name}")
    String consumerParam3(String str);
    
}
  1. 测试
package study.wyy.feign.java.test;

import org.junit.Before;
import org.junit.Test;
import study.wyy.feign.java.consumer.ParamClient;
import study.wyy.feign.java.spi.FeignClientBuilder;

import java.util.HashMap;
import java.util.Map;

/**
 * @author: wyaoyao
 * @date: 2020-12-17 14:00
 * @description: @RequestLine 测试
 */
public class ParamTest {
    private ParamClient client;
    @Before
    public void before(){
        client  = FeignClientBuilder.build(ParamClient.class);
    }
    @Test
    public void testConsumerParam1() {
        String result = client.consumerParam1("Feign");
        System.out.println(result);
    }

    @Test
    public void testConsumerParam2() {
        String s = client.consumerParam2("Feign");
        System.out.println(s);
    }

    @Test
    public void testConsumerParam3() {
        String s = client.consumerParam3("Feign");
        System.out.println(s);
    }

}
  1. 测试结果
    只有第一个可以测试通过,第二个第三个都是同一个错误:
[ParamClient#consumerParam3] ---> GET http://localhost:8001/feign/provider/param1 HTTP/1.1  # 参数根本没有拼接
[ParamClient#consumerParam3] Content-Length: 5
[ParamClient#consumerParam3] 
[ParamClient#consumerParam3] Feign
[ParamClient#consumerParam3] ---> END HTTP (5-byte body)
[ParamClient#consumerParam3] <--- HTTP/1.1 405 (18ms)
[ParamClient#consumerParam3] allow: GET
[ParamClient#consumerParam3] connection: keep-alive
[ParamClient#consumerParam3] content-type: application/json
[ParamClient#consumerParam3] date: Thu, 17 Dec 2020 07:54:20 GMT
[ParamClient#consumerParam3] keep-alive: timeout=60
[ParamClient#consumerParam3] transfer-encoding: chunked
[ParamClient#consumerParam3] 
[ParamClient#consumerParam3] {"timestamp":"2020-12-17T07:54:20.980+0000","status":405,"error":"Method Not Allowed","message":"Request method 'POST' not supported","path":"/feign/provider/param1"}
[ParamClient#consumerParam3] <--- END HTTP (166-byte body)

feign.FeignException$MethodNotAllowed: status 405 reading ParamClient#consumerParam3(String)

上面日志的输出可以看出
http://localhost:8001/feign/provider/param1 HTTP/1.1 # 参数根本没有拼接

所以@Param是不能省略的,即使形参的参数名字和模板中的一致也不可以省略

2.2.2 数组和集合
  1. 增加两个rest接口
   @GetMapping("/param2")
    public String param2(@RequestParam String[] names){
        return "@Param " + names;
    }

    @GetMapping("/param3")
    public String param3(@RequestParam List<String> names){
        return "@Param " + names;
    }
  1. Feign接口
@RequestLine("GET /feign/provider/param2?names={names}")
public String testArr(@Param("names") String[] names);

@RequestLine("GET /feign/provider/param3?names={names}")
public String testCollection(@Param("names") List<String> names);
  1. 测试
 @Test
    public void testArr(){
        System.out.println(client.testArr(new String[]{"java","python"}));
    }

    @Test
    public void testCollection(){
        List<String> list = new ArrayList<>();
        list.add("java");
        list.add("python");
        System.out.println(client.testCollection(list));
    }
  1. 测试结果
[ParamClient#testArr] ---> GET http://localhost:8001/feign/provider/param2?names=%5BLjava.lang.String&names=@47f37ef1 HTTP/1.1
[ParamClient#testArr] ---> END HTTP (0-byte body)
[ParamClient#testArr] <--- HTTP/1.1 200 (19ms)
[ParamClient#testArr] connection: keep-alive
[ParamClient#testArr] content-length: 35
[ParamClient#testArr] content-type: text/plain;charset=UTF-8
[ParamClient#testArr] date: Thu, 17 Dec 2020 08:17:17 GMT
[ParamClient#testArr] keep-alive: timeout=60
[ParamClient#testArr] 
[ParamClient#testArr] @Param [Ljava.lang.String;@36386bb1
[ParamClient#testArr] <--- END HTTP (35-byte body)
@Param [Ljava.lang.String;@36386bb1
[ParamClient#testCollection] ---> GET http://localhost:8001/feign/provider/param3?names=java&names=python HTTP/1.1
[ParamClient#testCollection] ---> END HTTP (0-byte body)
[ParamClient#testCollection] <--- HTTP/1.1 200 (19ms)
[ParamClient#testCollection] connection: keep-alive
[ParamClient#testCollection] content-length: 21
[ParamClient#testCollection] content-type: text/plain;charset=UTF-8
[ParamClient#testCollection] date: Thu, 17 Dec 2020 08:19:41 GMT
[ParamClient#testCollection] keep-alive: timeout=60
[ParamClient#testCollection] 
[ParamClient#testCollection] @Param [java, python]
[ParamClient#testCollection] <--- END HTTP (21-byte body)
@Param [java, python]

注意这两次方法请求格式

  • GET http://localhost:8001/feign/provider/param2?names=%5BLjava.lang.String&names=@47f37ef1
  • GET http://localhost:8001/feign/provider/param3?names=java&names=python

可以看到,如果是Collection类型是能够很好的被解析成多值的,但是数组不行,因此多用集合少用数组哦(数组直接调用toString()方法了)。

3 Headers注解

3.1 介绍

@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Headers {
  String[] value();
}

能标注在类上和方法上。用于传请求头,使用起来比较简单,形如这样即可:

@Headers({"Accept:*/*", "Accept-Language:zh-cn"})

唯一注意的一点:k-v使用的是:链接,而不是=。

3 QueryMap注解

@Retention(RUNTIME)
@java.lang.annotation.Target(PARAMETER)
public @interface QueryMap {
  boolean encoded() default false;
}

只能标注在方法参数上。用于传递多个查询值,拼接在URL后面,上面已经给出示例了。
仅需注意一点:只能标注在Map类型的参数前面,否则报错。

4 Body注解

4.1 介绍

前面都是get请求,那要是post请求呢,该如何向请求体中塞参数呢?
这就是@Body干的事了

@Target(METHOD)
@Retention(RUNTIME)
public @interface Body {
  String value();
}

@Body("{body}"),这样就可以通过方法参数的@Param(“body”) String body传值喽。注意:这个值最终是以http body体的形式发送的(并非URL参数哦),body体的内容并不要求必须是json,一般请配合请求头使用。

4.2 使用演示

  1. 定义个model
package study.wyy.feign.model;

import lombok.Data;
import lombok.ToString;

/**
 * @author: wyaoyao
 * @date: 2020-12-17 13:17
 * @description:
 */
@Data
@ToString
public class User {

    private String name;
    /**8
     * 年龄
     */
    private int age;

}
  1. 服务提供者定义一个Rest接口
package study.wyy.feign.provider.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import study.wyy.feign.model.User;

/**
 * @author: wyaoyao
 * @date: 2020-12-17 16:49
 * @description:
 */
@Slf4j
@RequestMapping("/feign/provider")
@RestController
public class BodyController {

    @PostMapping("body1")
    public String body1(@RequestBody User user) {
        if (null != user) {
            return "Hello " + user.getName();
        }
        return "no one";
    }
}
  1. 定义Feign的客户端接口
package study.wyy.feign.java.consumer;

import feign.Body;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import study.wyy.feign.model.User;

/**
 * @author: wyaoyao
 * @date: 2020-12-17 16:54
 * @description:
 */
public interface BodyClient {
    @Body("{person}")
    @RequestLine("POST /feign/provider/body1")
    String consumer1(@Param("person") User person);
}
  1. 测试
package study.wyy.feign.java.test;

import org.junit.Before;
import org.junit.Test;
import study.wyy.feign.java.consumer.BodyClient;
import study.wyy.feign.java.spi.FeignClientBuilder;
import study.wyy.feign.model.User;


/**
 *
 * @author: wyaoyao
 * @date: 2020-12-17 14:00
 * @description: @RequestLine 测试
 */
public class BodyTest {
    private BodyClient client;
    @Before
    public void before(){
        client  = FeignClientBuilder.build(BodyClient.class);
    }
    @Test
    public void test1() {
        User user = new User();
        user.setName("Feign");
        user.setAge(12);
        String result = client.consumer1(user);
        System.out.println(result);
    }
}
  1. 测试结果: 抛出Unsupported Media Type异常
[BodyClient#consumer1] ---> POST http://localhost:8001/feign/provider/body1 HTTP/1.1
[BodyClient#consumer1] Content-Length: 24
[BodyClient#consumer1] 
[BodyClient#consumer1] User(name=Feign, age=12)
[BodyClient#consumer1] ---> END HTTP (24-byte body)
[BodyClient#consumer1] <--- HTTP/1.1 415 (121ms)
[BodyClient#consumer1] connection: keep-alive
[BodyClient#consumer1] content-type: application/json
[BodyClient#consumer1] date: Thu, 17 Dec 2020 12:20:53 GMT
[BodyClient#consumer1] keep-alive: timeout=60
[BodyClient#consumer1] transfer-encoding: chunked
[BodyClient#consumer1] 
[BodyClient#consumer1] {"timestamp":"2020-12-17T12:20:53.165+0000","status":415,"error":"Unsupported Media Type","message":"Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported","path":"/feign/provider/body1"}
[BodyClient#consumer1] <--- END HTTP (210-byte body)

feign.FeignException$UnsupportedMediaType: status 415 reading BodyClient#consumer1(User)

因为我们服务提供者使用了@@RequestBody注解,所以请求要求"content-type要为application/json。所以呢要如何解决,前面可以通过@Headers来指定请求头,所以:进行如下修改:

@Body("{person}")
@RequestLine("POST /feign/provider/body1")
@Headers({"content-type:application/json"})
String consumer1(@Param("person") User person);
  1. 再次测试:这次是400
[BodyClient#consumer1] Content-Length: 24
[BodyClient#consumer1] content-type: application/json
[BodyClient#consumer1] 
[BodyClient#consumer1] User(name=Feign, age=12) # 原因就在这,Feign的默认序列化方式是调用ToString方法,toString方法返回的并不是一个Json格式
[BodyClient#consumer1] ---> END HTTP (24-byte body)
[BodyClient#consumer1] <--- HTTP/1.1 400 (40ms)
[BodyClient#consumer1] connection: close
[BodyClient#consumer1] content-type: application/json
[BodyClient#consumer1] date: Thu, 17 Dec 2020 12:42:06 GMT
[BodyClient#consumer1] transfer-encoding: chunked
[BodyClient#consumer1] 
[BodyClient#consumer1] {"timestamp":"2020-12-17T12:42:06.633+0000","status":400,"error":"Bad Request","message":"JSON parse error: Unrecognized token 'User': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false'); nested exception is com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'User': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')\n at [Source: (PushbackInputStream); line: 1, column: 6]","path":"/feign/provider/body1"}
[BodyClient#consumer1] <--- END HTTP (491-byte body)

feign.FeignException$BadRequest: status 400 reading BodyClient#consumer1(User)

User(name=Feign, age=12) :原因就在这,Feign的默认序列化方式是调用ToString方法,toString方法返回的并不是一个Json格式

  1. 验证一下使用json串是否可以
 @Body("{\"name\" : \"wyaoyao\"}") # 这里先直接写死一个json串
 @Headers({"Accept:*/*", "Accept-Language:    zh-cn","content-type:application/json"})
 @RequestLine("POST /feign/provider/body1")
 String consumer2();
@Test
public void test2() {

    String result = client.consumer2();
    System.out.println(result);
}

测试通过,那么如何解决这个问题,目前先提一个简单的方法,那就是重写toString方法,让其返回一个json串

 @Override
 public String toString() {
     String s = JSON.toJSONString(this);
     return s;
 }

再次测试就可以通过

[BodyClient#consumer1] ---> POST http://localhost:8001/feign/provider/body1 HTTP/1.1
[BodyClient#consumer1] Content-Length: 25
[BodyClient#consumer1] content-type: application/json
[BodyClient#consumer1] 
[BodyClient#consumer1] {"age":12,"name":"Feign"} # 这里的格式就是json格式啦
[BodyClient#consumer1] ---> END HTTP (25-byte body)
[BodyClient#consumer1] <--- HTTP/1.1 200 (17ms)
[BodyClient#consumer1] connection: keep-alive
[BodyClient#consumer1] content-length: 11
[BodyClient#consumer1] content-type: text/plain;charset=UTF-8
[BodyClient#consumer1] date: Thu, 17 Dec 2020 12:54:15 GMT
[BodyClient#consumer1] keep-alive: timeout=60
[BodyClient#consumer1] 
[BodyClient#consumer1] Hello Feign
[BodyClient#consumer1] <--- END HTTP (11-byte body)
Hello Feign

Feign默认情况下只能支持文本消息,但后来feign提供了feign-form这个扩展模块,所以也就能够支持二进制、文件上传喽。
需要说明的是:feign-form并不属于官方直接子模块,是后续新增的所以它的大版本号不跟主版本号走,GAV也有所不同:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值