⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 jianshu.com/p/08a5dd04093e 「一杯半盏」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

问题

在使用SpringCloud时,发现Feign不能发送POST表单请求

注意:在SpringBoot/Cloud环境中,使用的FeignClient,在不做额外设置的情况下,只能使用MVC的注解,也就是@RequestMapping和@RequestBody或者@RequestParam。

FeignClient 的 接口尝试以 POST表单发送请求的写法:

@RequestMapping(value="/someThing/someMethod", method=RequestMethod.POST)
ApiResponse someThing(@RequestParam("name") String value);

在没有改变默认Encoder的设置的情况下,FeignClient 对于简单类型的参数,例如String,Integer这些 wrapped primitive types,这种类型将被编码为form表单参数,即便写了method=RequestMethod.POST,但是参数没有出现在Body中,而是在URL上,不符合需求。

解决方案

<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<version>3.3.0</version>
</dependency>

对应FeignClient客户端

@FeignClient(name="remoteService",configuration = IRemoteService.FormSupportConfig.class)
public interface IRemoteService {
@RequestMapping(value="/someThing/someMethod",
method=RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
ApiResponse someThing(@RequestBody Map<String, ?> formParams);
}

class FormSupportConfig {

@Bean
public Encoder feignFormEncoder() {
return new SpringFormEncoder();
}
}
}

这样就可以发送表单请求了。

另一种不需要引入依赖的方法

List<NameValuePair> nvps = new ArrayList<>();
nvps.add(new BasicNameValuePair("key1", key1.toString()));
nvps.add(new BasicNameValuePair("key2", StringUtils.join(key2, ",")));
nvps.add(new BasicNameValuePair("key3", key3.toString()));
String queryStr = URLEncodedUtils.format(nvps, Consts.UTF_8);

someThing(queryStr);

对应

@RequestMapping(value="/someThing/someMethod", 
method=RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
ApiResponse someThing(@RequestBody String formParam);

推翻重来,上面的解决方案有问题

在同时使用表单和 RequestBody 的时候,使用上面的配置会抛异常,

is not a type supported by this encoder

将上面的configuration 改成这个类就没有问题了

import feign.codec.Encoder;
import feign.form.FormEncoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.cloud.netflix.feign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;

import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;

public class CoreFeignConfiguration {

@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;

@Bean
@Primary
@Scope(SCOPE_PROTOTYPE)
Encoder feignFormEncoder() {
return new FormEncoder(new SpringEncoder(this.messageConverters));
}
}

文章目录
  1. 1. 问题
  2. 2. 解决方案
  3. 3. 另一种不需要引入依赖的方法
  4. 4. 推翻重来,上面的解决方案有问题