日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

feign调用接口参数可以为null吗_FeignClient调用POST请求时查询参数被丢失的情况分析与处理...

發(fā)布時(shí)間:2024/7/19 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 feign调用接口参数可以为null吗_FeignClient调用POST请求时查询参数被丢失的情况分析与处理... 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

本文沒(méi)有詳細(xì)介紹 FeignClient 的知識(shí)點(diǎn),網(wǎng)上有很多優(yōu)秀的文章介紹了 FeignCient 的知識(shí)點(diǎn),在這里本人就不重復(fù)了,只是專注在這個(gè)問(wèn)題點(diǎn)上。

查詢參數(shù)丟失場(chǎng)景

業(yè)務(wù)描述: 業(yè)務(wù)系統(tǒng)需要更新用戶系統(tǒng)中的A資源,由于只想更新A資源的一個(gè)字段信息為B,所以沒(méi)有選擇通過(guò) entity 封裝B,而是直接通過(guò)查詢參數(shù)來(lái)傳遞B信息

文字描述:使用FeignClient來(lái)進(jìn)行遠(yuǎn)程調(diào)用時(shí),如果POST請(qǐng)求中有查詢參數(shù)并且沒(méi)有請(qǐng)求實(shí)體(body為空),那么查詢參數(shù)被丟失,服務(wù)提供者獲取不到查詢參數(shù)的值。

代碼描述:B的值被丟失,服務(wù)提供者獲取不到B的值

@FeignClient(name = "a-service", configuration = FeignConfiguration.class)

public interface ACall {

@RequestMapping(method = RequestMethod.POST, value = "/api/xxx/{A}", headers = {"Content-Type=application/json"})

void updateAToB(@PathVariable("A") final String A, @RequestParam("B") final String B) throws Exception;

}

問(wèn)題分析

背景

使用 FeignClient 客戶端

使用 feign-httpclient 中的 ApacheHttpClient 來(lái)進(jìn)行實(shí)際請(qǐng)求的調(diào)用

com.netflix.feign

feign-httpclient

8.18.0

直入源碼

通過(guò)對(duì) FeignClient 的源碼閱讀,發(fā)現(xiàn)問(wèn)題不是出在參數(shù)解析上,而是在使用 ApacheHttpClient 進(jìn)行請(qǐng)求時(shí),其將查詢參數(shù)放進(jìn)請(qǐng)求body中了,下面看源碼具體是如何處理的

feign.httpclient.ApacheHttpClient 這是 feign-httpclient 進(jìn)行實(shí)際請(qǐng)求的方法

@Override

public Response execute(Request request, Request.Options options) throws IOException {

HttpUriRequest httpUriRequest;

try {

httpUriRequest = toHttpUriRequest(request, options);

} catch (URISyntaxException e) {

throw new IOException("URL '" + request.url() + "' couldn't be parsed into a URI", e);

}

HttpResponse httpResponse = client.execute(httpUriRequest);

return toFeignResponse(httpResponse);

}

HttpUriRequest toHttpUriRequest(Request request, Request.Options options) throws

UnsupportedEncodingException, MalformedURLException, URISyntaxException {

RequestBuilder requestBuilder = RequestBuilder.create(request.method());

//per request timeouts

RequestConfig requestConfig = RequestConfig

.custom()

.setConnectTimeout(options.connectTimeoutMillis())

.setSocketTimeout(options.readTimeoutMillis())

.build();

requestBuilder.setConfig(requestConfig);

URI uri = new URIBuilder(request.url()).build();

requestBuilder.setUri(uri.getScheme() + "://" + uri.getAuthority() + uri.getRawPath());

//request query params

List queryParams = URLEncodedUtils.parse(uri, requestBuilder.getCharset().name());

for (NameValuePair queryParam: queryParams) {

requestBuilder.addParameter(queryParam);

}

//request headers

boolean hasAcceptHeader = false;

for (Map.Entry> headerEntry : request.headers().entrySet()) {

String headerName = headerEntry.getKey();

if (headerName.equalsIgnoreCase(ACCEPT_HEADER_NAME)) {

hasAcceptHeader = true;

}

if (headerName.equalsIgnoreCase(Util.CONTENT_LENGTH)) {

// The 'Content-Length' header is always set by the Apache client and it

// doesn't like us to set it as well.

continue;

}

for (String headerValue : headerEntry.getValue()) {

requestBuilder.addHeader(headerName, headerValue);

}

}

//some servers choke on the default accept string, so we'll set it to anything

if (!hasAcceptHeader) {

requestBuilder.addHeader(ACCEPT_HEADER_NAME, "*/*");

}

//request body

if (request.body() != null) {

//body為空,則HttpEntity為空

HttpEntity entity = null;

if (request.charset() != null) {

ContentType contentType = getContentType(request);

String content = new String(request.body(), request.charset());

entity = new StringEntity(content, contentType);

} else {

entity = new ByteArrayEntity(request.body());

}

requestBuilder.setEntity(entity);

}

//調(diào)用org.apache.http.client.methods.RequestBuilder#build方法

return requestBuilder.build();

}

org.apache.http.client.methods.RequestBuilder 此類是 HttpUriRequest 的Builder類,下面看build方法

public HttpUriRequest build() {

final HttpRequestBase result;

URI uriNotNull = this.uri != null ? this.uri : URI.create("/");

HttpEntity entityCopy = this.entity;

if (parameters != null && !parameters.isEmpty()) {

// 這里:如果HttpEntity為空,并且為POST請(qǐng)求或者為PUT請(qǐng)求時(shí),這個(gè)方法會(huì)將查詢參數(shù)取出來(lái)封裝成了HttpEntity

// 就是在這里查詢參數(shù)被丟棄了,準(zhǔn)確的說(shuō)是被轉(zhuǎn)換位置了

if (entityCopy == null && (HttpPost.METHOD_NAME.equalsIgnoreCase(method)

|| HttpPut.METHOD_NAME.equalsIgnoreCase(method))) {

entityCopy = new UrlEncodedFormEntity(parameters, charset != null ? charset : HTTP.DEF_CONTENT_CHARSET);

} else {

try {

uriNotNull = new URIBuilder(uriNotNull)

.setCharset(this.charset)

.addParameters(parameters)

.build();

} catch (final URISyntaxException ex) {

// should never happen

}

}

}

if (entityCopy == null) {

result = new InternalRequest(method);

} else {

final InternalEntityEclosingRequest request = new InternalEntityEclosingRequest(method);

request.setEntity(entityCopy);

result = request;

}

result.setProtocolVersion(this.version);

result.setURI(uriNotNull);

if (this.headergroup != null) {

result.setHeaders(this.headergroup.getAllHeaders());

}

result.setConfig(this.config);

return result;

}

解決方案

既然已經(jīng)知道原因了,那么解決方法就有很多種了,下面就介紹常規(guī)的解決方案:

使用 feign-okhttp 來(lái)進(jìn)行請(qǐng)求調(diào)用,這里就不列源碼了,感興趣大家可以去看, feign-okhttp 底層沒(méi)有判斷如果body為空則把查詢參數(shù)放入body中。

使用 io.github.openfeign:feign-httpclient:9.5.1 依賴,截取部分源碼說(shuō)明原因如下:

HttpUriRequest toHttpUriRequest(Request request, Request.Options options) throws

UnsupportedEncodingException, MalformedURLException, URISyntaxException {

RequestBuilder requestBuilder = RequestBuilder.create(request.method());

//省略部分代碼

//request body

if (request.body() != null) {

//省略部分代碼

} else {

// 此處,如果為null,則會(huì)塞入一個(gè)byte數(shù)組為0的對(duì)象

requestBuilder.setEntity(new ByteArrayEntity(new byte[0]));

}

return requestBuilder.build();

}

推薦的依賴

io.github.openfeign

feign-httpclient

9.5.1

或者

io.github.openfeign

feign-okhttp

9.5.1

總結(jié)

目前絕大部分的介紹 feign 的文章(本人所看到的,包括本人之前寫(xiě)的一篇文章也是)中都是推薦的 com.netflix.feign:feign-httpclient:8.18.0 和 com.netflix.feign:feign-okhttp:8.18.0 ,如果不巧你使用了 com.netflix.feign:feign-httpclient:8.18.0,那么在POST請(qǐng)求時(shí)并且body為空時(shí)就會(huì)發(fā)生丟失查詢參數(shù)的問(wèn)題。

這里推薦大家使用 feign-httpclient 或者是 feign-okhttp的時(shí)候不要依賴 com.netflix.feign,而應(yīng)該選擇 io.github.openfeign,因?yàn)榭雌饋?lái) Netflix 很久沒(méi)有對(duì)這兩個(gè)組件進(jìn)行維護(hù)了,而是由 OpenFeign 來(lái)進(jìn)行維護(hù)了。

參考資料:

總結(jié)

以上是生活随笔為你收集整理的feign调用接口参数可以为null吗_FeignClient调用POST请求时查询参数被丢失的情况分析与处理...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。