feign 调用第三方服务中部分特殊符号未转义问题

网友投稿 1255 2022-10-24

feign 调用第三方服务中部分特殊符号未转义问题

feign 调用第三方服务中部分特殊符号未转义问题

目录调用第三方部分特殊符号未转义1.问题发现过程2.解决办法3.疑问@RequestParams&符号未转义feign-core版本源码分析测试解决方案

调用第三方部分特殊符号未转义

开发过程中,发现+(加号)这个符号没有转义,导致再调用服务的时候把加号转义成空格了。导致后台获取到的数据会不正确。

1. 问题发现过程

feign 解析参数的时候,使用的标准是 RFC 3986,这个标准的加号是不需要http://被转义的。其具体的实现是 feign.template.UriUtils#encodeReserved(String value, String reserved, Charset charset)

2. 解决办法

feign 调用过程

1. feign核心先将(定义好的feign接口)接口中的参数解析出来

2. 对接实际参数和接口参数(入参调用的参数)

3. 对入参的参数进行编码(UriUtils#encodeReserved)(问题出在这里)

4. 调用注册的 RequestInterceptor(自定义)

5. Encoder 实现类,这里是body里面的内容才会有调用(自定义)

6. 具体的http网络请求逻辑

依据上面的过程,我们可以实现一个 RequestInterceptor -,在这里对参数再次进行转义即可。

public void apply(RequestTemplate template) {

Map> _queries = template.queries();

if (!_queries.isEmpty()) {

//由于在最新的 RFC 3986 规范,+号是不需要编码的,因此spring 实现的是这个规范,这里就需要参数中进行编码先,兼容旧规范。

Map> encodeQueries = new HashMap>(_queries.size());

Iterator iterator = _queries.keySet().iterator();

Collection encodeValues = null;

while (iterator.hasNext()) {

encodeValues = new ArrayList<>();

String key = iterator.next();

Collection values = _queries.get(key);

for (String _str : values) {

_str = _str.replaceAll("\\+", "%2B");

encodeValues.add(_str);

}

encodeQueries.put(key, encodeValues);

}

template.queries(null);

template.queries(encodeQueries);

}

}

上面是代码片段,详细请查看 FeignRequestInterceptor.java

3. 疑问

3.1 是否可以使用 HTTPClient 的实现就可以解决问题?

也不行,如果不做上面的实现,直接改用HTTPClient实现的话,也只是在发送的过程中起到作用,还是需要在前进行处理。

@RequestParams & 符号未转义

feign-core 版本

io.github.openfeign

feign-core

10.4.0

调用路径

源码分析

1.Template 类

package feign.template;

...

public class Template {

protected String resolveExpression(Expression expression, Map variables) {

String rehttp://solved = null;

Object value = variables.get(expression.getName());

// 1. 调用 SimpleExpression 的 expand() 方法

return expression.expand(value, this.encode.isEncodingRequired());

}

}

public final class Expressions {

static class SimpleExpression extends Expression {

private final FragmentType type;

String encode(Object value) {

// 2. 调用 UriUtils.encodeReserved() 方法,type 参数是 FragmentType.PATH_SEGMENT

return UriUtils.encodeReserved(value.toString(), type, Util.UTF_8);

}

@Override

String expand(Object variable, boolean encode) {

StringBuilder expanded = new StringBuilder();

expanded.append((encode) ? encode(variable) : variable);

String result = expanded.toString();

return result;

}

}

}

public class UriUtils {

public static String encodeReserved(String value, FragmentType type, Charset charset) {

return encodeChunk(value, type, charset);

}

private static String encodeChunk(String value, FragmentType type, Charset charset) {

byte[] data = value.getBytes(charset);

ByteArrayOutputStream encoded = new ByteArojNZEOMrayOutputStream();

for (byte b : data) {

if (type.isAllowed(b)) {

// 3.1 如果不需要转义,则不进行转义操作

encoded.write(b);

} else {

/* percent encode the byte */

// 3.2 否则,进行编码

pctEncode(b, encoded);

}

}

return new String(encoded.toByteArray());

}

enum FragmentType {

URI {

@Override

boolean isAllowed(int c) {

return isUnreserved(c);

}

},

PATH_SEGMENT {

@Override

boolean isAllowed(int c) {

return this.isPchar(c) || (c == '/');

}

}

abstract boolean isAllowed(int c);

protected boolean isAlpha(int c) {

return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z');

}

protected boolean isDigit(int c) {

return (c >= '0' && c <= '9');

}

protected boolean isSubDelimiter(int c) {

return (c == '!') || (c == '$') || (c == '&') || (c == '\'') || (c == '(') || (c == ')')

|| (c == '*') || (c == '+') || (c == ',') || (c == ';') || (c == '=');

}

protected boolean isUnreserved(int c) {

return this.isAlpha(c) || this.isDigit(c) || c == '-' || c == '.' || c == '_' || c == '~';

}

protected boolean isPchar(int c) {

return this.isUnreserved(c) || this.isSubDelimiter(c) || c == ':' || c == '@';

}

}

}

从源码上可以看出,& 字符属于 isSubDelimiter(),所以不会被转义。

测试

package feign.template;

import feign.Util;

public class UriUtilsDemo {

public static void main(String[] args) {

String str = "aa&aa";

// 输出:aa&aa

System.out.println(UriUtils.encodeReserved(str, UriUtils.FragmentType.PATH_SEGMENT, Util.UTF_8));

// 输出:aa%26aa

System.out.println(UriUtils.encodeReserved(str, UriUtils.FragmentType.URI, Util.UTF_8));

}

}

解决方案

1、升级 feign-core 版本,feign-core-10.12 已经没有这个问题。

2、使用 @RequestBody 替换 @RequestParam。

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

上一篇:优雅的Android MVP敏捷开发框架 - FDroid
下一篇:LeetCode第三题(Longest Substring Without Repeating Characters)三部曲之二:编码实现
相关文章

 发表评论

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