Feign form 表单提交的坑

1、背景

两个月前,刚入职新公司,需要 新启 一个工程 SDK, 做 三方接口 的转发,供多个部门使用。

三方的 接口 只能 接收 application/x-www-form-urlencoded ,不支持 json 参数

然而,接受的参数 有 下划线格式 (wan_id),很多接口的参数都 > 5个。

原本想想 SDK 做下 包装,把众多的参数都封装成对象,用 feign 调用。

刚使用 Jackson 来进行转换,但因为 接受端 不是 json 的格式,所以 入参 不能进行 下划线的转换。

因为只能接受 application/x-www-form-urlencoded。刚使用feign 就有点懵

2、查方案

查看 Encoder

查了很多文档,想看看 是否能在 encoder 的地方进行实现。当然失败告终,本身就不怎么熟悉,就直接尝试修改encoder,跨度太大了,方向有点问题

入参 使用 Map

想 Map作为参数 的方式 很通用, 调试 居然不行(不够仔细看文档导致)。我使用了 Map<String,Object> requestMap (这就是问题!!!) 。 试来试去不成功,郁闷了。

痛点是 参数 用下划线的 形式。用对象字段名 一样的话没有什么问题。

想来想去不应该,Map 传参数这么常见的 ,怎么不生效? 怀疑人生。 选择 了看源码debug。

​ 原来在进行 Map 判断的时候,发现了问题:

public void encode (Object object, Type bodyType, RequestTemplate template) throws EncodeException {
  String contentTypeValue = getContentTypeValue(template.headers());
  val contentType = ContentType.of(contentTypeValue);
  if (!processors.containsKey(contentType)) {
    delegate.encode(object, bodyType, template);
    return;
  }

  Map<String, Object> data;
  if (MAP_STRING_WILDCARD.equals(bodyType)) { //这里 专门对 Map 做了判断
    data = (Map<String, Object>) object;
  } else if (isUserPojo(bodyType)) {// 对象转换
    data = toMap(object);
  } else {
    delegate.encode(object, bodyType, template);
    return;
  }

  val charset = getCharset(contentTypeValue);
  processors.get(contentType).process(template, charset, data);
}
/**   这里 对  Map 做了  限制,只有 Map<String, ?> 才会相等
 * Type literal for {@code Map<String, ?>}.
 */
public static final Type MAP_STRING_WILDCARD =
    new Types.ParameterizedTypeImpl(null, Map.class, String.class,
        new Types.WildcardTypeImpl(new Type[] {Object.class}, new Type[0]));

这就是 Map 传参 没生效的 根本原因!!!!!

记录下 feign 使用的步骤

1、添加 pom

<dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-jackson</artifactId>
            <version>${feign.version}</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-core</artifactId>
            <version>${feign.version}</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
            <version>${feign.version}</version>
        </dependency>

        <dependency>
            <groupId>io.github.openfeign.form</groupId>
            <artifactId>feign-form</artifactId>
            <version>${feign-form.version}</version>
        </dependency>

        <dependency>
            <groupId>io.github.openfeign.form</groupId>
            <artifactId>feign-form-spring</artifactId>
            <version>${feign-form.version}</version>
        </dependency>

2、接口实现:

public interface XxxApiService {
		@Headers({CONTENT_TYPE + ":" + APPLICATION_FORM, ACCEPT + ":" + APPLICATION_JSON})
    @RequestLine(METHOD_POST + " /hello/{name}")
    ResponseBase demo(@Param("msg") String msg, @Param("name") String name);
}

3、定义property 中配置的 url等变量 (因为没有用到 服务注册,配置都是 在 property中进行切换)

@ConfigurationProperties(prefix = "xxx.api")
public class XxxApiProperties {

    private String url;

    private String key;
    }

4、构建 feign的代理bean:

@Bean
    public XxxApiService xxxApiService(okhttp3.OkHttpClient httpClient, XxxApiKeyInterceptor xxxApiKeyInterceptor, XxxHeaderInterceptor xxxHeaderInterceptor,
                                       XxxApiProperties properties) {
        return Feign.builder().client(new OkHttpClient(httpClient)).encoder(new FormEncoder(new JacksonEncoder())).queryMapEncoder(
                new BeanQueryMapEncoder()).decoder(new JacksonDecoder()).requestInterceptor(bmcApiKeyInterceptor).requestInterceptor(xxxHeaderInterceptor).target(
                XxxApiService.class, properties.getUrl());
    }

总结

1、feign form 表单 提交,Map 作为参数,必须 Map<String,?>。在查询过程中,当然看了官方文档和别人的博客,但就忽略了关键的点,这是需要改正的
2、现在的回顾,相对刚碰到问题时的记录,少了一些细节。需要多记录当时的一些 问题,及寻求方案的中间过程
3、以前对 feign,open feign, spring cloud openfeign 傻傻分不清,现在有了一点头绪。

引用 小马哥的 分享资料:

REST框架 使用场 景 请求映射注解 请求参数
Feign 客户端 声明 @RequestLine @Param
Spring Cloud Open Feign 客户端 声明 @ReqeustMapping @RequestParam
JAX-RS 客户端、服 务端 声明 @Path @*Param
Spring Web MVC 服 务端 声明 @ReqeustMapping @RequestParam
原文地址:https://www.cnblogs.com/idea-persistence/p/13470911.html