UriComponents

UriComponentsBuilder помогает создавать URI-идентификаторы из URI-шаблонов с переменными, как показано в следующем примере:

Java
UriComponents uriComponents = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")  
.queryParam("q", "{q}")  
.encode() 
.build(); 
URI uri = uriComponents.expand("Westin", "123").toUri();  
  1. Статический фабричный метод с URI-шаблоном.
  2. Добавляем или заменяем URI-компоненты.
  3. Запрос на кодирование URI-шаблона и URI-переменных.
  4. Собираем UriComponents.
  5. Расширяем переменные и получаем URI.
Kotlin
val uriComponents = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")  
.queryParam("q", "{q}")  
.encode() 
.build() 
val uri = uriComponents.expand("Westin", "123").toUri()  
  1. Статический фабричный метод с URI-шаблоном.
  2. Добавляем или заменяем URI-компоненты.
  3. Запрос на кодирование URI-шаблона и URI-переменных.
  4. Собираем UriComponents.
  5. Расширяем переменные и получаем URI.

Код из предыдущего примера можно объединить в одну цепочку и сократить с помощью buildAndExpand, как показано в следующем примере:

Java
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("Westin", "123")
.toUri();
Kotlin
val uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("Westin", "123")
.toUri()

Можно сократить его еще больше путем непосредственного перехода к URI-идентификатору (что подразумевает кодировку), как показано в следующем примере:

Java
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123");
Kotlin
val uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123")

Можно сократить его даже еще больше с помощью полного URI-шаблона, как показано в следующем примере:

Java
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
.build("Westin", "123");
Kotlin
val uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
.build("Westin", "123")

UriBuilder

UriComponentsBuilder реализует UriBuilder. В свою очередь, можно создать UriBuilder с помощью UriBuilderFactory. Вместе UriBuilderFactory и UriBuilder предоставляют подключаемый механизм для создания URI-идентификаторов из URI-шаблонов на основе общей конфигурации, такой как базовый URL-адрес, параметры кодировки и другие детали.

Можно сконфигурировать RestTemplate и WebClient с помощью UriBuilderFactory, чтобы настроить подготовку URI-идентификаторов. DefaultUriBuilderFactory - это реализация UriBuilderFactory по умолчанию, которая использует UriComponentsBuilder на внутреннем уровне и открывает общие параметры конфигурации.

В следующем примере показано, как сконфигурировать такой бин:

Java
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
String baseUrl = "https://example.org";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(factory);
Kotlin
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode
val baseUrl = "https://example.org"
val factory = DefaultUriBuilderFactory(baseUrl)
factory.encodingMode = EncodingMode.TEMPLATE_AND_VALUES
val restTemplate = RestTemplate()
restTemplate.uriTemplateHandler = factory

В следующем примере конфигурируется WebClient:

Java
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
String baseUrl = "https://example.org";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
Kotlin
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode
val baseUrl = "https://example.org"
val factory = DefaultUriBuilderFactory(baseUrl)
factory.encodingMode = EncodingMode.TEMPLATE_AND_VALUES
val client = WebClient.builder().uriBuilderFactory(factory).build()

Кроме того, можно использовать DefaultUriBuilderFactory напрямую. Это похоже на использование UriComponentsBuilder, но вместо статических фабричных методов, это реальный экземпляр, который хранит конфигурацию и параметры, как показано в следующем примере:

Java
String baseUrl = "https://example.com";
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);
URI uri = uriBuilderFactory.uriString("/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123");
Kotlin
val baseUrl = "https://example.com"
val uriBuilderFactory = DefaultUriBuilderFactory(baseUrl)
val uri = uriBuilderFactory.uriString("/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123")

Кодирование URI

UriComponentsBuilder открывает опции кодирования на двух уровнях:

  • UriComponentsBuilder#encode(): Сначала предварительно кодирует URI-шаблон, а затем строго кодирует URI-переменные при расширении.

  • UriComponents#encode(): Кодирует URI-компоненты после расширения URI-переменных.

Обе опции заменяют символы, не относящиеся к стандарту ASCII, и недопустимые символы на экранированные октеты. Однако первый вариант также заменяет символы с зарезервированным значением, которые появляются в переменных.

Рассмотрим ";", который является допустимым в пути, но имеет зарезервированное значение. Первый вариант заменяет ";" на "%3B" в переменных, но не в URI-шаблоне. В отличие от этого, второй вариант никогда не заменяет ";", поскольку он является допустимым символом в пути.

В большинстве случаев первый вариант, скорее всего, даст ожидаемый результат, поскольку он учитывает URI-переменные как непрозрачные данные, которые должны быть полностью закодированы, в то время как второй вариант полезен, если URI-переменные намеренно содержат зарезервированные символы. Второй вариант также работает, если не расширять URI-переменные вообще, поскольку в этом случае кодируется все, что случайным образом будет похоже на URI-переменную.

В следующем примере используется первый вариант:

Java
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("New York", "foo+bar")
.toUri();
// Результат "/hotel%20list/New%20York?q=foo%2Bbar"
Kotlin
val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("New York", "foo+bar")
.toUri()
// Результат "/hotel%20list/New%20York?q=foo%2Bbar"

Можно сократить код в предыдущем примере путем непосредственного перехода к URI-идентификатору (что подразумевает кодировку), как показано в следующем примере:

Java
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.build("New York", "foo+bar");
Kotlin
val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.build("New York", "foo+bar")

Можно сократить его даже еще больше с помощью полного URI-шаблона, как показано в следующем примере:

Java
URI uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
.build("New York", "foo+bar");
Kotlin
val uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
.build("New York", "foo+bar")

WebClient и RestTemplate расширяют и кодируют URI-шаблоны на внутреннем уровне с помощью стратегии UriBuilderFactory. Оба варианта можно сконфигурировать с помощью кастомной стратегии, как показано в следующем примере:

Java
String baseUrl = "https://example.com";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl)
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
// Настраиваем RestTemplate...
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(factory);
// Настраиваем WebClient...
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
Kotlin
val baseUrl = "https://example.com"
val factory = DefaultUriBuilderFactory(baseUrl).apply {
encodingMode = EncodingMode.TEMPLATE_AND_VALUES
}
// Настраиваем RestTemplate...
val restTemplate = RestTemplate().apply {
uriTemplateHandler = factory
}
// Настраиваем WebClient...
val client = WebClient.builder().uriBuilderFactory(factory).build()

Реализация DefaultUriBuilderFactory использует UriComponentsBuilder на внутреннем уровне для расширения и кодирования URI-шаблонов. Как фабрика, она предоставляет единое место для конфигурирования подхода к кодированию, основанного на одном из перечисленных ниже режимов кодирования:

  • TEMPLATE_AND_VALUES: Использует UriComponentsBuilder#encode (), соответствующий первой опции в предыдущем списке, для предварительного кодирования URI-шаблона и строгого кодирования переменных при расширении.

  • VALUES_ONLY: Не кодирует URI-шаблон и, вместо этого, применяет строгое кодирование к переменным URI-идентификаторов через UriUtils#encodeUriVariables перед тем, как расширить их в шаблон.

  • URI_COMPONENT: Использует UriComponents#encode(), соответствующий второму варианту в предыдущем списке, для кодирования значения компонента URI-идентификатора после расширения URI-переменных.

  • NONE Кодирование не применяется.

RestTemplate установлен в EncodingMode.URI_COMPONENT по историческим причинам и для обратной совместимости. WebClient обращается к значению по умолчанию в DefaultUriBuilderFactory, которое было изменено с EncodingMode.URI_COMPONENT в 5.0.x на EncodingMode.TEMPLATE_AND_VALUES в 5.1.