feign调用中文参数被encode编译的问题

网友投稿 781 2022-08-23


feign调用中文参数被encode编译的问题

目录Feign调用中文参数被encode编译原因记录今天遇到的feign多参数问题1.Post方式2.Get方式

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);


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:#yyds干货盘点#交换设备丢包严重的故障处理
下一篇:#yyds干货盘点#python抽象工厂模式
相关文章

 发表评论

暂时没有评论,来抢沙发吧~