UriComponents
UriComponentsBuilder помогает создавать URI-идентификаторы из URI-шаблонов с переменными, как показано в следующем примере:
UriComponents uriComponents = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.encode()
.build();
URI uri = uriComponents.expand("Westin", "123").toUri();
- Статический фабричный метод с URI-шаблоном.
- Добавляем или заменяем URI-компоненты.
- Запрос на кодирование URI-шаблона и URI-переменных.
- Собираем
UriComponents. - Расширяем переменные и получаем
URI.
val uriComponents = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.encode()
.build()
val uri = uriComponents.expand("Westin", "123").toUri()
- Статический фабричный метод с URI-шаблоном.
- Добавляем или заменяем URI-компоненты.
- Запрос на кодирование URI-шаблона и URI-переменных.
- Собираем
UriComponents. - Расширяем переменные и получаем
URI.
Код из предыдущего примера можно объединить в одну цепочку и сократить с помощью buildAndExpand, как показано в следующем примере:
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("Westin", "123")
.toUri();
val uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("Westin", "123")
.toUri()
Можно сократить его еще больше путем непосредственного перехода к URI-идентификатору (что подразумевает кодировку), как показано в следующем примере:
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123");
val uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123")
Можно сократить его даже еще больше с помощью полного URI-шаблона, как показано в следующем примере:
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
.build("Westin", "123");
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 на внутреннем уровне и открывает общие параметры конфигурации.
В следующем примере показано, как сконфигурировать такой бин:
// 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);
// 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:
// 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();
// 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, но вместо статических фабричных методов, это реальный экземпляр, который хранит конфигурацию и параметры, как показано в следующем примере:
String baseUrl = "https://example.com";
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);
URI uri = uriBuilderFactory.uriString("/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123");
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, и недопустимые символы на экранированные октеты. Однако первый вариант также заменяет символы с зарезервированным значением, которые появляются в переменных.
В большинстве случаев первый вариант, скорее всего, даст ожидаемый результат, поскольку он учитывает URI-переменные как непрозрачные данные, которые должны быть полностью закодированы, в то время как второй вариант полезен, если URI-переменные намеренно содержат зарезервированные символы. Второй вариант также работает, если не расширять URI-переменные вообще, поскольку в этом случае кодируется все, что случайным образом будет похоже на URI-переменную.
В следующем примере используется первый вариант:
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("New York", "foo+bar")
.toUri();
// Результат "/hotel%20list/New%20York?q=foo%2Bbar"
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-идентификатору (что подразумевает кодировку), как показано в следующем примере:
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.build("New York", "foo+bar");
val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.build("New York", "foo+bar")
Можно сократить его даже еще больше с помощью полного URI-шаблона, как показано в следующем примере:
URI uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
.build("New York", "foo+bar");
val uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
.build("New York", "foo+bar")
WebClient и RestTemplate расширяют и кодируют URI-шаблоны на внутреннем уровне с помощью стратегии UriBuilderFactory. Оба варианта можно сконфигурировать с помощью кастомной стратегии, как показано в следующем примере:
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();
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.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ