feign調(diào)用中文參數(shù)被encode編譯的問(wèn)題
Feign調(diào)用中文參數(shù)被encode編譯
原因
在實(shí)現(xiàn)一個(gè)feign調(diào)用時(shí)使用了Post請(qǐng)求,并且拼接url參數(shù),name傳值為中文時(shí)被encode轉(zhuǎn)譯,且最終接取數(shù)據(jù)之前未被decode轉(zhuǎn)譯回,問(wèn)題探索:
feign:
@FeignClient(name = "service-test") public interface TestServiceApi { ? ? @PostMapping("/test/abc") ? ? public String getTestNo(@RequestParam("code") String code, @RequestParam("name") String name); }
controller:
@RequestMapping("/test") public interface TestController { ?? ?@Autowired ?? ?private TestService testService; ? ? @PostMapping("/abc") ? ? public String getTestNo(@RequestParam("code") String code, @RequestParam("name") String name) { ? ? ? ? return testService.query(code, name);? ? ? } ? ? }
在controller中接到的中文參數(shù)被URIEncode轉(zhuǎn)譯了
測(cè)試 被轉(zhuǎn)譯成:%E6%B5%8B%E8%AF%95
找到feign的入口類ReflectiveFeign中拼裝RequestTemplate的方法:
? ? @Override ? ? public RequestTemplate create(Object[] argv) { ? ? ? RequestTemplate mutable = new RequestTemplate(metadata.template()); ? ? ? if (metadata.urlIndex() != null) { ? ? ? ? int urlIndex = metadata.urlIndex(); ? ? ? ? checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex); ? ? ? ? mutable.insert(0, String.valueOf(argv[urlIndex])); ? ? ? } ? ? ? Map<String, Object> varBuilder = new LinkedHashMap<String, Object>(); ? ? ? for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) { ? ? ? ? int i = entry.getKey(); ? ? ? ? Object value = argv[entry.getKey()]; ? ? ? ? if (value != null) { // Null values are skipped. ? ? ? ? ? if (indexToExpander.containsKey(i)) { ? ? ? ? ? ? value = expandElements(indexToExpander.get(i), value); ? ? ? ? ? } ? ? ? ? ? for (String name : entry.getValue()) { ? ? ? ? ? ? varBuilder.put(name, value); ? ? ? ? ? } ? ? ? ? } ? ? ? } ? ? ? // 組裝template的方法 ? ? ? RequestTemplate template = resolve(argv, mutable, varBuilder); ? ? ? if (metadata.queryMapIndex() != null) { ? ? ? ? // add query map parameters after initial resolve so that they take ? ? ? ? // precedence over any predefined values ? ? ? ? template = addQueryMapQueryParameters(argv, template); ? ? ? } ? ? ? if (metadata.headerMapIndex() != null) { ? ? ? ? template = addHeaderMapHeaders(argv, template); ? ? ? } ? ? ? return template; ? ? }
在RequestTemplate類中如果是拼接在url后的param那么會(huì)被使用encodeValueIfNotEncoded都encode轉(zhuǎn)譯,但是不會(huì)走decode的方法
/** ? ?* Resolves any template parameters in the requests path, query, or headers against the supplied ? ?* unencoded arguments. <br> <br><br><b>relationship to JAXRS 2.0</b><br> <br> This call is ? ?* similar to {@code javax.ws.rs.client.WebTarget.resolveTemplates(templateValues, true)} , except ? ?* that the template values apply to any part of the request, not just the URL ? ?*/ ? RequestTemplate resolve(Map<String, ?> unencoded, Map<String, Boolean> alreadyEncoded) { ? ? replaceQueryValues(unencoded, alreadyEncoded); ? ? Map<String, String> encoded = new LinkedHashMap<String, String>(); ? ? for (Entry<String, ?> entry : unencoded.entrySet()) { ? ? ? final String key = entry.getKey(); ? ? ? final Object objectValue = entry.getValue(); ? ? ? String encodedValue = encodeValueIfNotEncoded(key, objectValue, alreadyEncoded); ? ? ? encoded.put(key, encodedValue); ? ? } ? ? String resolvedUrl = expand(url.toString(), encoded).replace("+", "%20"); ? ? if (decodeSlash) { ? ? ? resolvedUrl = resolvedUrl.replace("%2F", "/"); ? ? } ? ? url = new StringBuilder(resolvedUrl); ? ? Map<String, Collection<String>> resolvedHeaders = new LinkedHashMap<String, Collection<String>>(); ? ? for (String field : headers.keySet()) { ? ? ? Collection<String> resolvedValues = new ArrayList<String>(); ? ? ? for (String value : valuesOrEmpty(headers, field)) { ? ? ? ? String resolved = expand(value, unencoded); ? ? ? ? resolvedValues.add(resolved); ? ? ? } ? ? ? resolvedHeaders.put(field, resolvedValues); ? ? } ? ? headers.clear(); ? ? headers.putAll(resolvedHeaders); ? ? if (bodyTemplate != null) { ? ? ? body(urlDecode(expand(bodyTemplate, encoded))); ? ? } ? ? return this; ? }
如果傳入的值在requestBody中,則不會(huì)被encode轉(zhuǎn)譯
? ? @Override ?? ?public void encode(Object requestBody, Type bodyType, RequestTemplate request) ?? ??? ??? ?throws EncodeException { ?? ??? ?// template.body(conversionService.convert(object, String.class)); ?? ??? ?if (requestBody != null) { ?? ??? ??? ?Class<?> requestType = requestBody.getClass(); ?? ??? ??? ?Collection<String> contentTypes = request.headers().get("Content-Type"); ?? ??? ??? ?MediaType requestContentType = null; ?? ??? ??? ?if (contentTypes != null && !contentTypes.isEmpty()) { ?? ??? ??? ??? ?String type = contentTypes.iterator().next(); ?? ??? ??? ??? ?requestContentType = MediaType.valueOf(type); ?? ??? ??? ?} ?? ??? ??? ?for (HttpMessageConverter<?> messageConverter : this.messageConverters ?? ??? ??? ??? ??? ?.getObject().getConverters()) { ?? ??? ??? ??? ?if (messageConverter.canWrite(requestType, requestContentType)) { ?? ??? ??? ??? ??? ?if (log.isDebugEnabled()) { ?? ??? ??? ??? ??? ??? ?if (requestContentType != null) { ?? ??? ??? ??? ??? ??? ??? ?log.debug("Writing [" + requestBody + "] as \"" ?? ??? ??? ??? ??? ??? ??? ??? ??? ?+ requestContentType + "\" using [" ?? ??? ??? ??? ??? ??? ??? ??? ??? ?+ messageConverter + "]"); ?? ??? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ??? ?else { ?? ??? ??? ??? ??? ??? ??? ?log.debug("Writing [" + requestBody + "] using [" ?? ??? ??? ??? ??? ??? ??? ??? ??? ?+ messageConverter + "]"); ?? ??? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ?FeignOutputMessage outputMessage = new FeignOutputMessage(request); ?? ??? ??? ??? ??? ?try { ?? ??? ??? ??? ??? ??? ?@SuppressWarnings("unchecked") ?? ??? ??? ??? ??? ??? ?HttpMessageConverter<Object> copy = (HttpMessageConverter<Object>) messageConverter; ?? ??? ??? ??? ??? ??? ?copy.write(requestBody, requestContentType, outputMessage); ?? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ?catch (IOException ex) { ?? ??? ??? ??? ??? ??? ?throw new EncodeException("Error converting request body", ex); ?? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ?// clear headers ?? ??? ??? ??? ??? ?request.headers(null); ?? ??? ??? ??? ??? ?// converters can modify headers, so update the request ?? ??? ??? ??? ??? ?// with the modified headers ?? ??? ??? ??? ??? ?request.headers(getHeaders(outputMessage.getHeaders())); ?? ??? ??? ??? ??? ?// do not use charset for binary data ?? ??? ??? ??? ??? ?if (messageConverter instanceof ByteArrayHttpMessageConverter) { ?? ??? ??? ??? ??? ??? ?request.body(outputMessage.getOutputStream().toByteArray(), null); ?? ??? ??? ??? ??? ?} else { ?? ??? ??? ??? ??? ??? ?request.body(outputMessage.getOutputStream().toByteArray(), Charset.forName("UTF-8")); ?? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ?return; ?? ??? ??? ??? ?} ?? ??? ??? ?} ?? ??? ??? ?String message = "Could not write request: no suitable HttpMessageConverter " ?? ??? ??? ??? ??? ?+ "found for request type [" + requestType.getName() + "]"; ?? ??? ??? ?if (requestContentType != null) { ?? ??? ??? ??? ?message += " and content type [" + requestContentType + "]"; ?? ??? ??? ?} ?? ??? ??? ?throw new EncodeException(message); ?? ??? ?} ?? ?}
綜合上述的調(diào)試,如果在Post中拼接參數(shù)那么會(huì)被encode轉(zhuǎn)譯,且不會(huì)被decode轉(zhuǎn)譯,如果使用body傳參,那么不會(huì)出現(xiàn)轉(zhuǎn)譯問(wèn)題,如果必須使用拼接傳參,那么可以使用方法
1. @RequestLine的注解自定義參數(shù)的格式,具體參考該注解的使用方式。
2.在Feign的RequestInterceptor將傳遞的值decode的擴(kuò)展方法。
記錄今天遇到的feign多參數(shù)問(wèn)題
1.Post方式
錯(cuò)誤寫法示例如下:
public int save(@RequestBody final User u, @RequestBody final School s);
錯(cuò)誤原因:
fegin中可以有多個(gè)@RequestParam,但只能有不超過(guò)一個(gè)@RequestBody,@RequestBody用來(lái)修飾對(duì)象,但是既有@RequestBody也有@RequestParam,
那么參數(shù)就要放在請(qǐng)求的Url中,@RequestBody修飾的就要放在提交對(duì)象中。
注意?。。?用來(lái)處理@RequestBody Content-Type 為 application/json,application/xml編碼的內(nèi)容
正確寫法示例如下:
public int save(@RequestBody final Person p,@RequestParam("userId") String userId,@RequestParam("userTel") String userTel);
2.Get方式
錯(cuò)誤寫法示例如下:
@RequestMapping(value="/test", method=RequestMethod.GET) ? Model test(final String name, ?final int age);?
錯(cuò)誤原因:
異常原因:當(dāng)使用Feign時(shí),如果發(fā)送的是get請(qǐng)求,那么需要在請(qǐng)求參數(shù)前加上@RequestParam注解修飾,Controller里面可以不加該注解修飾,@RequestParam可以修飾多個(gè),@RequestParam是用來(lái)修飾參數(shù),不能用來(lái)修飾整個(gè)對(duì)象。
注意:@RequestParam Content-Type 為 application/x-www-form-urlencoded 而這種是默認(rèn)的
正確寫法示例如下:
@GetMapping("/getSchoolDetail") ? ? public ResultMap getSchoolDetail(@RequestParam("kSchoolId") LongkSchoolId, ? ? ?@RequestParam("kSchoolYearId") Long kSchoolYearId);
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java動(dòng)態(tài)代理實(shí)現(xiàn)代碼
這篇文章主要介紹了java 動(dòng)態(tài)代理的的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下,希望能給你帶來(lái)幫助2021-07-07淺談在springboot中使用定時(shí)任務(wù)的方式
今天給大家?guī)?lái)的是關(guān)于Java的相關(guān)知識(shí),文章圍繞著在springboot中使用定時(shí)任務(wù)的方式展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06Spring?Boot項(xiàng)目抵御XSS攻擊實(shí)戰(zhàn)過(guò)程
XSS攻擊又稱跨站腳本攻擊,通常指利用網(wǎng)頁(yè)開發(fā)時(shí)留下的漏洞,通過(guò)巧妙的方法注入惡意指令代碼到網(wǎng)頁(yè),使用戶加載并執(zhí)行攻擊者惡意制造的網(wǎng)頁(yè)程序,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot項(xiàng)目抵御XSS攻擊的相關(guān)資料,需要的朋友可以參考下2022-11-11java多線程處理執(zhí)行solr創(chuàng)建索引示例
這篇文章主要介紹了java多線程處理執(zhí)行solr創(chuàng)建索引示例,需要的朋友可以參考下2014-02-02Java如何導(dǎo)出數(shù)據(jù)庫(kù)中的所有數(shù)據(jù)表到指定文件夾
這篇文章主要介紹了Java導(dǎo)出數(shù)據(jù)庫(kù)中的所有數(shù)據(jù)表到指定文件夾,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06用Java程序判斷是否是閏年的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)?lái)一篇用Java程序判斷是否是閏年的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-06-06Maven的pom.xml文件結(jié)構(gòu)中的build
本文主要介紹了Maven的pom.xml文件結(jié)構(gòu)中的build,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07AsyncHttpClient IOExceptionFilter異常過(guò)濾器
這篇文章主要為大家介紹了AsyncHttpClient IOExceptionFilter異常過(guò)濾器代碼流程解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12