千家信息网

如何解决feign调用中文参数被encode编译的问题

发表于:2025-01-22 作者:千家信息网编辑
千家信息网最后更新 2025年01月22日,这篇文章主要介绍如何解决feign调用中文参数被encode编译的问题,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Feign调用中文参数被encode编译原因在实现一个fei
千家信息网最后更新 2025年01月22日如何解决feign调用中文参数被encode编译的问题

这篇文章主要介绍如何解决feign调用中文参数被encode编译的问题,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

Feign调用中文参数被encode编译

原因

在实现一个feign调用时使用了Post请求,并且拼接url参数,name传值为中文时被encode转译,且最终接取数据之前未被decode转译回,问题探索:

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中接到的中文参数被URIEncode转译了

测试 被转译成:%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 varBuilder = new LinkedHashMap();      for (Entry> 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转译,但是不会走decode的方法

 /**   * Resolves any template parameters in the requests path, query, or headers against the supplied   * unencoded arguments. 


relationship to JAXRS 2.0

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 unencoded, Map alreadyEncoded) { replaceQueryValues(unencoded, alreadyEncoded); Map encoded = new LinkedHashMap(); for (Entry 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> resolvedHeaders = new LinkedHashMap>(); for (String field : headers.keySet()) { Collection resolvedValues = new ArrayList(); 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转译

    @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 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 copy = (HttpMessageConverter) 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);        }    }

综合上述的调试,如果在Post中拼接参数那么会被encode转译,且不会被decode转译,如果使用body传参,那么不会出现转译问题,如果必须使用拼接传参,那么可以使用方法

1. @RequestLine的注解自定义参数的格式,具体参考该注解的使用方式。

2.在Feign的RequestInterceptor将传递的值decode的扩展方法。

记录今天遇到的feign多参数问题

1.Post方式

错误写法示例如下:

public int save(@RequestBody final User u, @RequestBody final School s);

错误原因:

fegin中可以有多个@RequestParam,但只能有不超过一个@RequestBody,@RequestBody用来修饰对象,但是既有@RequestBody也有@RequestParam,

那么参数就要放在请求的Url中,@RequestBody修饰的就要放在提交对象中。

注意!!! 用来处理@RequestBody Content-Type 为 application/json,application/xml编码的内容

正确写法示例如下:

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时,如果发送的是get请求,那么需要在请求参数前加上@RequestParam注解修饰,Controller里面可以不加该注解修饰,@RequestParam可以修饰多个,@RequestParam是用来修饰参数,不能用来修饰整个对象。

注意:@RequestParam Content-Type 为 application/x-www-form-urlencoded 而这种是默认的

正确写法示例如下:

@GetMapping("/getSchoolDetail")    public ResultMap getSchoolDetail(@RequestParam("kSchoolId") LongkSchoolId,     @RequestParam("kSchoolYearId") Long kSchoolYearId);

以上是"如何解决feign调用中文参数被encode编译的问题"这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注行业资讯频道!

0