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 |