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 |