整个错位来自于以下事实:存在如何将空间编码/解码为非标准实践"+"
.
可以说空间可以(正在)被编码成"+"
or "%20"
.
例如,Google 对搜索字符串执行此操作:
https://www.google.com/search?q=test+my+space+delimited+entry
rfc1866, section-8.2.2 https://www.rfc-editor.org/rfc/rfc1866#section-8.2.2指出 GET 请求的查询部分应编码为'application/x-www-form-urlencoded'
.
所有表单的默认编码是`application/x-www-form-
urlencoded'。表单数据集在此媒体类型中表示为
如下:
- 表单字段名称和值被转义:space
字符被替换为“+”.
另一方面rfc3986 https://www.rfc-editor.org/rfc/rfc3986规定 URL 中的空格必须使用编码"%20"
.
这基本上意味着有不同的标准来编码空格,具体取决于它们在 URI 中的位置语法成分 https://www.rfc-editor.org/rfc/rfc3986#page-16.
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/\_________/ \_________/ \__/
| | | | |
scheme authority path query fragment
| _____________________|__
/ \ / \
urn:example:animal:ferret:nose
基于这些评论,我们可以在 URI 中的 GET http 调用中声明:
- 之前有空格
"?"
需要编码为"%20"
- 之后的空格
"?"
查询参数中需要编码为"+"
- 意思是
"+"
符号需要编码为"%2B"
在查询参数中
Spring实现遵循rfc规范,所以这就是为什么当你发送“+412386789”在查询参数中,"+"
符号被解释为空白字符,它到达后端为“412386789”.
看着:
final URI uri = UriComponentsBuilder.fromHttpUrl("http://localhost")
.port(port)
.path("/events")
.queryParams(params)
.build()
.toUri();
你会发现:
"foo#bar@quizz+foo-bazz//quir."
被编码为"foo%23bar@quizz+foo-bazz//quir."
符合规范(rfc3986 https://www.rfc-editor.org/rfc/rfc3986).
所以如果你想要"+"
查询参数中的 char 不被解释为空格,您需要将其编码为"%2B"
.
您发送到后端的参数应如下所示:
params.add("id", id);
params.add("device", device);
params.add("phoneNumber", "%2B225697845");
params.add("timestamp", "2019-03-25T15%3A09%3A44.703088%2B02%3A00");
params.add("value", "foo%23bar%40quizz%2Bfoo-bazz%2F%2Fquir.");
为了做到这一点,你可以使用UrlEncoder https://docs.oracle.com/javase/8/docs/api/index.html?java/net/URLEncoder.html将参数传递给地图时。当心 UriComponentsBuilder 对你的东西进行双重编码!
您可以通过以下方式获得正确的 URL:
final MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("id", id);
params.add("device", device);
String uft8Charset = StandardCharsets.UTF_8.toString();
params.add("phoneNumber", URLEncoder.encode(phoneNumber, uft8Charset));
params.add("timestamp", URLEncoder.encode(timestamp.toString(), uft8Charset));
params.add("value", URLEncoder.encode(value, uft8Charset));
final URI uri = UriComponentsBuilder.fromHttpUrl("http://localhost")
.port(port)
.path("/events")
.queryParams(params)
.build(true)
.toUri();
请注意,将“true”传递给build()
方法关闭编码,因此这意味着 URI 部分中的方案、主机等将无法正确编码UriComponentsBuilder https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/util/UriComponentsBuilder.html.