这个接口的职责就将对象编码编码到Http请求体中。当参数没有标注@Param注解时候,编码器就会生效
添加链接描述
public interface Encoder {
// 变量输入到Map,表示要编码的对象是一个表单
Type MAP_STRING_WILDCARD = Util.MAP_STRING_WILDCARD;
// 唯一接口方法:object 需要被编码的对象(有可能是POJO,有可能是字符串)
// bodyType:body类型
// template:请求模版
void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;
}
@RequestLine("POST /")
String login(@Param("username") String username, @Param("password") String password);
class Default implements Encoder {
@Override
public void encode(Object object, Type bodyType, RequestTemplate template) {
// 1、若bodyType是String类型,那就把object直接toString()后放进去即可 这是特殊的处理...
// 2、若是字节数组类型,那就强转放进去喽
// 3、否则就报错
if (bodyType == String.class) {
template.body(object.toString());
} else if (bodyType == byte[].class) {
template.body((byte[]) object, null);
} else if (object != null) {
throw new EncodeException(
format("%s is not a type supported by this encoder.", object.getClass()));
}
}
}
@PostMapping("/encoder1")
public String test1(String name,Integer num){
return "name: " + name + " num: " + num;
}
在默认实现的方法入口处打个断点。
/****
* 都标注有@Param注解,并且都被模版使用了
*/
@RequestLine("POST /feign/provider/encoder1?name={name}&num={num}")
String test1(@Param("name") String name, @Param("num") Integer num);
这种情况Encoder接口不会生效,断点没有进去
/****
* 都标注有@Param注解,但模版只使用一个
*/
@RequestLine("POST /feign/provider/encoder1?name={name}")
String test2(@Param("name") String name, @Param("num") Integer num);
测试
@Test
public void test2 (){
String s = client.test2("wyy", 2020);
System.out.println("========test2end==============");
}
会报错:
feign.codec.EncodeException: class java.util.LinkedHashMap is not a type supported by this encoder.
at feign.codec.Encoder$Default.encode(Encoder.java:94)
模板中没有用到的参数num,被封装到一个Map中但是默认编码器,不支持Map类型,默认的支持字符串和字节数组
/****
* 都标注有@Param注解,但模版都没有使用
*/
@RequestLine("GET /feign/provider/encoder1")
String test3(@Param("name") String name, @Param("num") Integer num);
@Test
public void test3 (){
String s = client.test3("wyy", 2020);
System.out.println(s);
System.out.println("========tes3end=============");
}
错误也是如此:
feign.codec.EncodeException: class java.util.LinkedHashMap is not a type supported by this encoder.
at feign.codec.Encoder$Default.encode(Encoder.java:94)
总结1:
如果存在形参被@Param注解标注了,但是在@RequestLine模板中没有配置该key,这些未被模板使用的参数就会被收集到一个Map交给编码器处理。但是默认不支持处理Map
/****
* 不标注@Pram注解,是String类型
*/
@RequestLine("POST /feign/provider/encoder1")
String test4(String name);
@Test
public void test4 (){
String s = client.test4("wyy");
System.out.println(s);
System.out.println("========test4end============");
}
[EncoderClient#test4] ---> POST http://localhost:8001/feign/provider/encoder1 HTTP/1.1
[EncoderClient#test4] Content-Length: 3
[EncoderClient#test4]
[EncoderClient#test4] wyy # 被放到了请求body中
[EncoderClient#test4] ---> END HTTP (3-byte body)
[EncoderClient#test4] <--- HTTP/1.1 200 (34ms)
[EncoderClient#test4] connection: keep-alive
[EncoderClient#test4] content-length: 20
[EncoderClient#test4] content-type: text/plain;charset=UTF-8
[EncoderClient#test4] date: Mon, 22 Mar 2021 06:32:10 GMT
[EncoderClient#test4] keep-alive: timeout=60
[EncoderClient#test4]
[EncoderClient#test4] name: null num: null
[EncoderClient#test4] <--- END HTTP (20-byte body)
name: null num: null
这个没有使用Param注解的参数会被编码器处理器,由于是String类型,默认实现是支持该类型的,所以成功放到请求体中。
这个都没有使用param注解和body注解,就可以把String类型的数据放到请求体中,这就是编码器吃的功劳
所以这个请求是没有问题的,只是我们服务端使用SpringMVC接收URL参数的,所以放到body参数没有取处理,所以响应回来的数据是null
测试一下,将上面的controller改一下:
@PostMapping("/encoder1")
public String test1(@RequestBody String name,Integer num){
return "name: " + name + " num: " + num;
}
在测试:
[EncoderClient#test4] ---> POST http://localhost:8001/feign/provider/encoder1 HTTP/1.1
[EncoderClient#test4] Content-Length: 3
[EncoderClient#test4]
[EncoderClient#test4] wyy
[EncoderClient#test4] ---> END HTTP (3-byte body)
[EncoderClient#test4] <--- HTTP/1.1 200 (190ms)
[EncoderClient#test4] connection: keep-alive
[EncoderClient#test4] content-length: 20
[EncoderClient#test4] content-type: text/plain;charset=UTF-8
[EncoderClient#test4] date: Mon, 22 Mar 2021 12:07:24 GMT
[EncoderClient#test4] keep-alive: timeout=60
[EncoderClient#test4]
[EncoderClient#test4] name: wyy= num: null
[EncoderClient#test4] <--- END HTTP (20-byte body)
name: wyy= num: null 发现wyy成功响应回来了
========test4end============
/****
* 不标注@Pram注解,不是String类型
*/
@RequestLine("POST /feign/provider/encoder1")
String test5(Integer num);
测试发现:又是这个熟悉的错误
feign.codec.EncodeException: class java.lang.Integer is not a type supported by this encoder.
at feign.codec.Encoder$Default.encode(Encoder.java:94)
这个没有使用Param注解的参数会被编码器处理器,由于不是String类型或者Byte数组类型,所以报错。
@PostMapping("/encoder2")
public String test2( User user){
return user.toString();
}
/****
* 标注@Pram注解,是POJO
*/
@RequestLine("POST /feign/provider/encoder")
String test6(@Param("user") User user);
@Test
public void test6 (){
User user = new User();
user.setName("wyy");
user.setAge(2020);
String s = client.test6(user);
System.out.println(s);
System.out.println("========test5end===============");
}
feign.codec.EncodeException: class study.wyy.feign.model.User is not a type supported by this encoder.
at feign.codec.Encoder$Default.encode(Encoder.java:94)
这个使用Param注解的参数会被编码器处理器,由于不是String类型或者Byte数组类型,所以报错。
/****
* 不标注@Pram注解,是POJO
*/
@RequestLine("POST /feign/provider/encoder2")
String test7(User user);
feign.codec.EncodeException: class study.wyy.feign.model.User is not a type supported by this encoder.
at feign.codec.Encoder$Default.encode(Encoder.java:94)
发现也是这个错误。
但是和上一个错误区别在于这里是User对象,而不是Map这里是因为不支持User对象所以抛错
常用的编码器是不能满足我们常用的json的数据格式的,所以这里自定义一个编码器
@Data
public class User implements Serializable {
private String name;
/**8
* 年龄
*/
private Integer age;
private List<String> hobby;
@PostMapping("/encoder3")
public String test3(@RequestBody User user){
return user.toString();
}
/**
或者使用了Param注解的参数,但是没有模板引用该参数,如果支持该类型的参数(默认就是字符串和字节数组),放到请求体中。
**/
@RequestLine("POST /feign/provider/encoder3")
@Headers({"content-type:application/json"})
String test8(@Param("user") User user);
/**
- 编码器会处理没有使用Param注解的参数,如果支持该类型的参数(默认就是字符串和字节数组),放到请求体中
**/
@RequestLine("POST /feign/provider/encoder3")
@Headers({"content-type:application/json"})
String test9(User user);
public class JsonEncoder implements Encoder {
private final ObjectMapper MAPPER = new ObjectMapper();
@SneakyThrows
@Override
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
if(object != null){
String json = MAPPER.writer().writeValueAsString(object);
System.out.println(json);
// 放到body中
template.body(Request.Body.bodyTemplate(json, StandardCharsets.UTF_8));
}
}
}
@Test
public void test8 (){
User user = new User();
user.setName("wyy");
user.setAge(2020);
EncoderClient encoderClient = FeignClientBuilder.buildJsonEncoder(EncoderClient.class);
String s = encoderClient.test8(user);
System.out.println(s);
System.out.println("========test6end==========");
}
@Test
public void test9 (){
User user = new User();
user.setName("wyy");
user.setAge(2020);
EncoderClient encoderClient = FeignClientBuilder.buildJsonEncoder(EncoderClient.class);
String s = encoderClient.test9(user);
System.out.println(s);
System.out.println("========test6end==========");
}
第一个测试,会把参数收集到map中的,所以json数据不对,到服务提供者的时候无法反序列化为User对象。所以响应结果为null
{"user":{"name":"wyy","age":2020,"hobby":null}}
[EncoderClient#test8] ---> POST http://localhost:8001/feign/provider/encoder3 HTTP/1.1
[EncoderClient#test8] Content-Length: 47
[EncoderClient#test8] content-type: application/json
[EncoderClient#test8]
[EncoderClient#test8] {"user":{"name":"wyy","age":2020,"hobby":null}}
[EncoderClient#test8] ---> END HTTP (47-byte body)
[EncoderClient#test8] <--- HTTP/1.1 200 (104ms)
[EncoderClient#test8] connection: keep-alive
[EncoderClient#test8] content-length: 37
[EncoderClient#test8] content-type: text/plain;charset=UTF-8
[EncoderClient#test8] date: Mon, 22 Mar 2021 13:42:42 GMT
[EncoderClient#test8] keep-alive: timeout=60
[EncoderClient#test8]
[EncoderClient#test8] User(name=null, age=null, hobby=null)
[EncoderClient#test8] <--- END HTTP (37-byte body)
User(name=null, age=null, hobby=null)
========test6end==========
json数据格式:{“user”:{“name”:“wyy”,“age”:2020,“hobby”:null}}
第一个测试,就不会把参数收集到map中的,所以响应回来的json数据是对的。
{"name":"wyy","age":2020,"hobby":null}
[EncoderClient#test9] ---> POST http://localhost:8001/feign/provider/encoder3 HTTP/1.1
[EncoderClient#test9] Content-Length: 38
[EncoderClient#test9] content-type: application/json
[EncoderClient#test9]
[EncoderClient#test9] {"name":"wyy","age":2020,"hobby":null}
[EncoderClient#test9] ---> END HTTP (38-byte body)
[EncoderClient#test9] <--- HTTP/1.1 200 (21ms)
[EncoderClient#test9] connection: keep-alive
[EncoderClient#test9] content-length: 36
[EncoderClient#test9] content-type: text/plain;charset=UTF-8
[EncoderClient#test9] date: Mon, 22 Mar 2021 13:44:12 GMT
[EncoderClient#test9] keep-alive: timeout=60
[EncoderClient#test9]
[EncoderClient#test9] User(name=wyy, age=2020, hobby=null)
[EncoderClient#test9] <--- END HTTP (36-byte body)
User(name=wyy, age=2020, hobby=null)
========test6end==========
切记不能这么写,因为这么写,编码器就不生效了,这个写法,既使用了param注解,模板中也用了这个参数,不满足上面说的两个条件。
@RequestLine("POST /feign/provider/encoder3")
@Headers({"content-type:application/json"})
@Body("{user}")
String test10(@Param("user")User user);
查询映射将使用的POJO可访问的getter属性方法最后作为查询参数拼接上去,拼接的顺序并不保证,如果某个属性为null,将不会拼接。
默认使用的是FieldQueryMapEncoder,效果和BeanQueryMapEncoder一样,只是现在推荐使用BeanQueryMapEncoder
/**
* pojo
*
* @return
*/
@RequestLine("GET /feign/provider/get/pojo/")
String invokeTest05(@QueryMap User user);
最后生成的url:
GET http://localhost:8001/feign/provider/get/pojo/?name=Wade&age=13&hobby=basketball&hobby=football