feign調(diào)用中文參數(shù)被encode編譯的問題
Feign調(diào)用中文參數(shù)被encode編譯
原因
在實現(xiàn)一個feign調(diào)用時使用了Post請求,并且拼接url參數(shù),name傳值為中文時被encode轉(zhuǎn)譯,且最終接取數(shù)據(jù)之前未被decode轉(zhuǎ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)譯了
測試 被轉(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那么會被使用encodeValueIfNotEncoded都encode轉(zhuǎn)譯,但是不會走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中,則不會被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ù)那么會被encode轉(zhuǎn)譯,且不會被decode轉(zhuǎn)譯,如果使用body傳參,那么不會出現(xiàn)轉(zhuǎn)譯問題,如果必須使用拼接傳參,那么可以使用方法
1. @RequestLine的注解自定義參數(shù)的格式,具體參考該注解的使用方式。
2.在Feign的RequestInterceptor將傳遞的值decode的擴展方法。
記錄今天遇到的feign多參數(shù)問題
1.Post方式
錯誤寫法示例如下:
public int save(@RequestBody final User u, @RequestBody final School s);
錯誤原因:
fegin中可以有多個@RequestParam,但只能有不超過一個@RequestBody,@RequestBody用來修飾對象,但是既有@RequestBody也有@RequestParam,
那么參數(shù)就要放在請求的Url中,@RequestBody修飾的就要放在提交對象中。
注意?。?! 用來處理@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方式
錯誤寫法示例如下:
@RequestMapping(value="/test", method=RequestMethod.GET) ? Model test(final String name, ?final int age);?
錯誤原因:
異常原因:當使用Feign時,如果發(fā)送的是get請求,那么需要在請求參數(shù)前加上@RequestParam注解修飾,Controller里面可以不加該注解修飾,@RequestParam可以修飾多個,@RequestParam是用來修飾參數(shù),不能用來修飾整個對象。
注意:@RequestParam Content-Type 為 application/x-www-form-urlencoded 而這種是默認的
正確寫法示例如下:
@GetMapping("/getSchoolDetail") ? ? public ResultMap getSchoolDetail(@RequestParam("kSchoolId") LongkSchoolId, ? ? ?@RequestParam("kSchoolYearId") Long kSchoolYearId);
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Spring?Boot項目抵御XSS攻擊實戰(zhàn)過程
XSS攻擊又稱跨站腳本攻擊,通常指利用網(wǎng)頁開發(fā)時留下的漏洞,通過巧妙的方法注入惡意指令代碼到網(wǎng)頁,使用戶加載并執(zhí)行攻擊者惡意制造的網(wǎng)頁程序,下面這篇文章主要給大家介紹了關于Spring?Boot項目抵御XSS攻擊的相關資料,需要的朋友可以參考下2022-11-11java多線程處理執(zhí)行solr創(chuàng)建索引示例
這篇文章主要介紹了java多線程處理執(zhí)行solr創(chuàng)建索引示例,需要的朋友可以參考下2014-02-02Java如何導出數(shù)據(jù)庫中的所有數(shù)據(jù)表到指定文件夾
這篇文章主要介紹了Java導出數(shù)據(jù)庫中的所有數(shù)據(jù)表到指定文件夾,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-06-06Maven的pom.xml文件結(jié)構(gòu)中的build
本文主要介紹了Maven的pom.xml文件結(jié)構(gòu)中的build,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-07-07AsyncHttpClient IOExceptionFilter異常過濾器
這篇文章主要為大家介紹了AsyncHttpClient IOExceptionFilter異常過濾器代碼流程解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12