Все смещение происходит из-за того, что существует нестандартная практика, как кодировать / декодировать пространство в "+"
.
Возможно, пространство может (сейчас) кодироваться в "+"
или "%20"
.
Например, Google делает это со строками поиска:
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
На основании этих замечаний мы можем утверждать, что в вызовах GET httpв URI:
- пробелов до
"?"
необходимо кодировать в "%20"
- пробелов после
"?"
в параметрах запроса нужно кодировать в "+"
- , что означает, что
"+"
знаки должны быть закодированы в "%2B"
в параметрах запроса
Реализация Spring соответствует спецификациям rfc, поэтому при отправке "+ 412386789« в параметрах запроса знак "+"
интерпретируется как пробелchar и он получает к бэкэнду как "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
).
Итак, если вы хотите, чтобы символ "+"
в параметрах вашего запроса не интерпретировался как пробел, вынеобходимо закодировать его в "%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
.