Кодирование 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");
    }
}
Другие вопросы по тегам