首页 > 解决方案 > 正确解析包含“+”字符的字段

问题描述

我遇到了一个奇怪的情况,我在https://github.com/lgueye/uri-parameters-behavior中复制了它

由于我们在方法中请求我们的一个后端时迁移到spring-boot 2 ( spring framework 5GET ),我们遇到了以下情况:所有带有 char 的字段在到达后端时+都被更改为(whitespace) char

以下值已更改:

我在 stackoverflow ( https://github.com/spring-projects/spring-framework/issues/14464#issuecomment-453397378 ) 和 github ( https://github.com/spring-projects/spring -框架/问题/21577

我已经实现了一个 mockMvc 单元测试和一个集成测试

单元测试正常运行集成测试失败(如我们的生产)

任何人都可以帮我解决这个问题吗?我的目标显然是让集成测试通过。

谢谢您的帮助。

路易斯

标签: spring-bootspring-mvcrfc3986

解决方案


整个错位来自这样一个事实,即如何将空间编码/解码为"+".

可以说,空间可以(正在)编码为"+"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”。表单数据集在此媒体类型中表示
如下:

  1. 表单字段名称和值被转义:空格 字符替换为 '+'

另一方面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.


推荐阅读