spring-boot - 正确解析包含“+”字符的字段
问题描述
我遇到了一个奇怪的情况,我在https://github.com/lgueye/uri-parameters-behavior中复制了它
由于我们在方法中请求我们的一个后端时迁移到spring-boot 2 ( spring framework 5GET
),我们遇到了以下情况:所有带有 char 的字段在到达后端时+
都被更改为(whitespace) char
以下值已更改:
- +412386789(电话号码)转入** 412386789**
- 2019-03-22T17:18:39.621+02:00 (java8 ZonedDateTime) 进入2019-03-22T17:18:39.621 02:00(导致org.springframework.validation.BindException
我在 stackoverflow ( https://github.com/spring-projects/spring-framework/issues/14464#issuecomment-453397378 ) 和 github ( https://github.com/spring-projects/spring -框架/问题/21577)
我已经实现了一个 mockMvc 单元测试和一个集成测试
单元测试正常运行集成测试失败(如我们的生产)
任何人都可以帮我解决这个问题吗?我的目标显然是让集成测试通过。
谢谢您的帮助。
路易斯
解决方案
整个错位来自这样一个事实,即如何将空间编码/解码为"+"
.
可以说,空间可以(正在)编码为"+"
or "%20"
。
例如,谷歌对搜索字符串这样做:
https://www.google.com/search?q=test+my+space+delimited+entry
rfc1866, section-8.2.2
声明 GET 请求的查询部分应编码为'application/x-www-form-urlencoded'
.
所有表单的默认编码都是“application/x-www-form-
urlencoded”。表单数据集在此媒体类型中表示
如下:
- 表单字段名称和值被转义:空格 字符替换为 '+'。
另一方面rfc3986
指出,URL 中的空格必须使用"%20"
.
这基本上意味着对空格进行编码有不同的标准,具体取决于它们在 URI语法组件中的位置。
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
)。
因此,如果您希望"+"
查询参数中的 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
在将参数传递给地图时使用。当心 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
.
推荐阅读
- python - 如何从不同数量的输入中进行递归排列以在 Python 中工作?
- java - 如何在 Kotlin 中 * 在 * 类构造之后初始化变量?
- authentication - Django-Rest-Framwork 中的会话身份验证,这真的是我必须做的才能使 CSRF 安全吗?
- node.js - NodeJS - PostgreSQL - 10 并发脚本返回错误
- bash - 为什么在符号链接目录下的相对路径..用不同的命令定义不同?
- r - 一起使用 tidyverse 和 reticulate 会导致 R 会话崩溃
- scala - 如何为 akka.grpc 启用 gRPC 日志记录?
- python - 检查我的图像是否在 pygame 中碰撞的问题
- python - 有没有办法将变量与列表中的每个项目进行比较?
- webdriver-io - 在 webdriverIO v7 中使用 $ 函数收到警告“参数类型字符串不可分配给任何参数类型”