Tips
@QueryMap
feign.QueryMap#encoded
encoded=true时,表明参数值已经编码过了,不需要feign去编码;encoded=false,默认值,需要feign对参数进行urlencode
当我们不希望对查询参数进行urlencode编码时可使用该注解
发送content-type为application/x-www-form-urlencoded的请求
设置org.springframework.web.bind.annotation.PostMapping#consumes
属性
@PostMapping(value = "xxxx/xxxx", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
String getOpenId(@RequestParam String accessToken, @RequestBody OpenIdRequest request);
feignclient请求流程
feign.ReflectiveFeign.FeignInvocationHandler#invoke
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("equals".equals(method.getName())) { // ... } // ... return dispatch.get(method).invoke(args); }
feign.SynchronousMethodHandler#invoke
构建请求模板对象RequestTemplate,然后发送请求并对响应解码
编码Encoder/Decoder
feign.codec.Encoder
在上述构建RequestTemplate的过程中,涉及对参数编码的逻辑
@Override
public RequestTemplate create(Object[] argv) {
RequestTemplate mutable = RequestTemplate.from(metadata.template());
// ...省略部分代码
RequestTemplate template = resolve(argv, mutable, varBuilder);
// ...
return template;
使用编码器Encoder对参数进行编码
@Override
protected RequestTemplate resolve(Object[] argv,
RequestTemplate mutable,
Map<String, Object> variables) {
// ...
if (alwaysEncodeBody) {
body = argv == null ? new Object[0] : argv;
// 使用编码器
encoder.encode(body, Object[].class, mutable);
} else {
encoder.encode(body, metadata.bodyType(), mutable);
}
// ...
return super.resolve(argv, mutable, variables);
}
Encoder实现类的来源:①在feignclient的自动配置类中向容器中注入了Encoder
的实现类SpringEncoder
的实例。②也可以利用@FeignClient的configuration属性指定局部配置类,在局部配置类中自定义Encoder的实现类。③在配置文件中为某个feignclient指定使用的encoder
进入SpringEncoder#encode
,如果content-type是表单数据(multipart/form-data
、application/x-www-form-urlencoded
等)则使用SpringFormEncoder
进行编码,否则使用消息转换器HttpMessageConverter
对参数进行处理
例:content-type是application/x-www-form-urlencoded
,则使用SpringFormEncoder
编码器去编码,最终调用其父类FormEncoder
的encode方法,该方法会使用UrlencodedFormContentProcessor
来处理
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
String contentTypeValue = this.getContentTypeValue(template.headers());
ContentType contentType = ContentType.of(contentTypeValue);
if (!this.processors.containsKey(contentType)) {
this.delegate.encode(object, bodyType, template);
} else {
// ...省略部分代码
Charset charset = this.getCharset(contentTypeValue);
// 交给ContentProcessor处理
((ContentProcessor)this.processors.get(contentType)).process(template, charset, data);
}
}
feign.codec.Decoder
与Encoder
类似
容错fallback
拦截器RequestInterceptor
扩展
x-www-form-urlencoded
传输表单数据,这种编码方式的请求数据会放置在请求体发送。
例如使用这种content-type提交以下内容:
name: Susan
password: 666777
会被编码成以下内容在请求体中进行传输
name=Susan&password=666777
multipart/form-data
这种格式会把表单内容分成多个部分,这也是为什么叫multipart,而且每个part都支持不同的内容类型,当然请求头中的content-type肯定是multipart/form-data
例如可以通过表单在一次请求中传输文本类型的参数和文件类型的参数
注意:并不是必须要用request.getInputStream()
方法才能读取请求体中的数据,如果是表单数据,也可以用request.getParameter()
方法获取参数,就是说request.getParameter()
方法除了能获取请求地址上的参数,还能获取表单中的参数(x-www-form-urlencode和form-data),这些表单数据是放在请求体中传输的。但是使用form-data上传文件时,无法使用request.getParameter()
获取
对于x-www-form-urlencode和form-data这两种格式,request.getParameter()
方法会从请求体中读取数据,底层也是通过获取InputStream
实现的
动态URL(未验证)
@feignclient修饰的接口中,方法参数使用java.net.URI
@FeignClient
的url属性不能为空,可使用任意字符串作为占位避免报错