Кодирование URL с использованием нового Spring UriComponentsBuilder
Я пытаюсь использовать Spring UriComponentsBuilder, чтобы сгенерировать некоторые URL-адреса для взаимодействия oauth. Параметры запроса включают в себя такие объекты, как URL-адреса обратного вызова и значения параметров с пробелами в них.
Попытка использовать UriComponentBuilder (потому что UriUtils теперь устарела)
UriComponentsBuilder urlBuilder = UriComponentsBuilder.fromHttpUrl(oauthURL);
urlBuilder.queryParam("client_id", clientId);
urlBuilder.queryParam("redirect_uri", redirectURI);
urlBuilder.queryParam("scope", "test1 test2");
String url = urlBuilder.build(false).encode().toUriString();
К сожалению, хотя пространство в параметре области успешно заменено на '+', параметр redirect_uri вообще не закодирован в URL.
Например,
redirect_uri=https://oauth2-login-demo.appspot.com/code
должен был в конечном итоге
redirect_uri=https%3A%2F%2Foauth2-login-demo.appspot.com%2Fcode
но был нетронут. Погружение в код, в частности org.springframework.web.util.HierarchicalUriComponents.Type.QUERY_PARAM.isAllowed(c):
if ('=' == c || '+' == c || '&' == c) {
return false;
}
else {
return isPchar(c) || '/' == c || '?' == c;
}
явно допускает символы ':' и '/', чего, по словам жевательной резинки, не должно. Должно быть, он использует какой-то другой тип кодирования, хотя я не представляю, что для меня. Я лаю здесь не те деревья?
Спасибо
5 ответов
UriComponentsBuilder кодирует ваш URI в соответствии с RFC 3986 (см. http://www.ietf.org/rfc/rfc3986.txt, в частности раздел 3.4, в котором описывается компонент запроса URI).
В компоненте 'query' символы '/' и ':' разрешены и не нуждаются в экранировании.
Например, символ "/": компонент "запрос" (который четко ограничен неэкранированными символами "?" И (необязательно) "#") не является иерархическим, и символ "/" не имеет специального значения. Так что это не нуждается в кодировании.
Насколько я понимаю, UriComponentsBuilder не кодирует параметры запроса автоматически, а только оригинальный HttpUrl, с которым он был создан. Другими словами, вы все равно должны явно кодировать:
String redirectURI= "https://oauth2-login-demo.appspot.com/code";
urlBuilder.queryParam("redirect_uri", URLEncoder.encode(redirectURI,"UTF-8" ));
Попробуйте отсканировать документ UriComponentsBuilder, есть метод с именем build(в булевском кодировании)
Пример кода 1:
UriComponents uriComponents = UriComponentsBuilder.fromPath("/path1/path2").build(true);
Вот мой пример кода 2:
UriComponents uriComponents = UriComponentsBuilder.newInstance()
.scheme("https")
.host("my.host")
.path("/path1/path2").query(parameters).build(true);
URI uri= uriComponents.toUri();
ResponseEntity<MyEntityResponse> responseEntity = restTemplate.exchange(uri,
HttpMethod.GET, entity, typeRef);
Я пробовал все вышеперечисленные решения, пока не заработал.
В моем примере я пытался закодировать формат ZonedDateTime.
2022-01-21T10:17:10.228+06:00
. Плюсик был проблемой.
Что решило мою проблему, так это кодирование значения вручную + использование URI вместо строкового значения (оба были очень важны).
До:
restTemplate.exchange(
UriComponentsBuilder
.queryParam("fromDateTime", "2022-01-21T10:17:10.228+06:00")
.build()
.toUriString(),
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<MyDto>>() {}
);
После:
restTemplate.exchange(
UriComponentsBuilder
.queryParam("fromDateTime", URLEncoder.encode("2022-01-21T10:17:10.228+06:00", StandardCharsets.UTF_8))
.build(true)
.toUri(),
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<MyDto>>() {}
);
я поставил передbuild()
чтобы кодировка параметров запроса работала для меня. Эти тесты сравниваются с тестами безencode()
вызов.
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;
public class EncodeQueryParametersTest {
@Test
public void withoutEncode() {
final MultiValueMap<String, String> queryParameters = new LinkedMultiValueMap<>();
queryParameters.add("fullname", "First Last");
assertThat(UriComponentsBuilder.newInstance().queryParams(queryParameters).build().getQuery()).isEqualTo("fullname=First Last");
}
@Test
public void withEncode() {
final MultiValueMap<String, String> queryParameters = new LinkedMultiValueMap<>();
queryParameters.add("fullname", "First Last");
assertThat(UriComponentsBuilder.newInstance().queryParams(queryParameters).encode().build().getQuery()).isEqualTo("fullname=First%20Last");
}
}