Spring Web Reactive | 1. Spring WebFlux | 1.6. URI Links

Web MVC

This section describes the various options available in the Spring Framework for preparing URIs.

1.6.1. UriComponents

Spring MVC and Spring WebFlux

UriComponentsBuilder can create a URI from a URI template with variables, as shown in the following example.

Java

UriComponents uriComponents = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}")     // (1) 
        .queryParam("q", "{q}")     // (2) 
        .encode()    // (3) 
        .build();    // (4) 

URI uri = uriComponents.expand("Westin", "123").toUri();     // (5) 

Kotlin

UriComponents uriComponents = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}")    // (1) 
        .queryParam("q", "{q}")    // (2) 
        .encode()   // (3) 
        .build();   // (4) 

URI uri = uriComponents.expand("Westin", "123").toUri();     // (5) 
  • (1) A static factory method using a URI template.
  • (2) Add or replace URI components.
  • (3) Request encoding of the URI template and URI variables.
  • (4) Build UriComponents.
  • (5) Expand the variables and obtain the URI.

The example above can be combined into one chain and shortened with buildAndExpand, as shown in the following example.

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()

The following example can be shortened further by going directly to the URI, which implies encoding.

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")

You can shorten it even more by using a complete URI template, as shown in the following example.

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}?q={q}")
    .build("Westin", "123")

1.6.2. UriBuilder

Spring MVC and Spring WebFlux

UriComponentsBuilder implements UriBuilder. You can create a UriBuilder by using UriBuilderFactory. UriBuilderFactory and UriBuilder provide a pluggable mechanism for building URIs from URI templates according to shared configuration, such as base URL, encoding settings, and other details.

You can configure RestTemplate and WebClient with UriBuilderFactory to customize URI preparation. DefaultUriBuilderFactory uses UriComponentsBuilder internally and is the default implementation of UriBuilderFactory that exposes shared configuration options.

The following example shows how to configure RestTemplate.

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

The following example configures 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()

You can also use DefaultUriBuilderFactory directly. It is similar to using UriComponentsBuilder, but as shown in the following example, it is an actual instance that holds configuration settings instead of static factory methods.

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")

1.6.3. URI Encoding

Spring MVC and Spring WebFlux

UriComponentsBuilder provides encoding options at two levels.

Both options change non-ASCII characters and invalid characters into escaped octets. However, the first option also replaces characters with reserved meaning that appear in URI variables.

Consider “;”. This character is valid in a path, but its meaning is reserved. The first option replaces “;” so a URI variable contains %3B, but it does not replace “;” in the URI template. By contrast, the second option does not replace “;” because it is a valid character in the path.

In most cases, the first option gives the desired result because it treats URI variables as opaque data that is fully encoded. The second option is useful when URI variables intentionally contain reserved characters. The second option is also helpful when URI variables are not expanded at all, because it also encodes strings that accidentally look like URI variables.

The following example uses the first option.

Java

URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
        .queryParam("q", "{q}")
        .encode()
        .buildAndExpand("New York", "foo+bar")
        .toUri();

// Result is "/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()

// Result is "/hotel%20list/New%20York?q=foo%2Bbar"

As in the following example, you can reduce the example above by going directly to the URI, which implies encoding.

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")

You can shorten it even more by using a complete URI template, as shown in the following example.

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 and RestTemplate internally expand and encode URI templates through the UriBuilderFactory strategy. Both can be configured with a custom strategy, as in the following example.

Java

String baseUrl = "https://example.com";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl)
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);

// Customize the RestTemplate..
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(factory);

// Customize the WebClient..
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();

Kotlin

val baseUrl = "https://example.com"
val factory = DefaultUriBuilderFactory(baseUrl).apply {
    encodingMode = EncodingMode.TEMPLATE_AND_VALUES
}

// Customize the RestTemplate..
val restTemplate = RestTemplate().apply {
    uriTemplateHandler = factory
}

// Customize the WebClient..
val client = WebClient.builder().uriBuilderFactory(factory).build()

The DefaultUriBuilderFactory implementation uses UriComponentsBuilder internally to expand and encode URI templates. As a factory, it provides a single place to configure the encoding approach based on one of the following encoding modes.

  • TEMPLATE_AND_VALUES: Uses UriComponentsBuilder#encode(), corresponding to the first option in the previous list, to pre-encode the URI template and strictly encode URI variables when they are expanded.
  • VALUES_ONLY: Does not encode the URI template. Instead, it uses UriUtils#encodeUriVariables to apply exact encoding to URI variables before expanding them into the template.
  • URI_COMPONENT: Uses UriComponents#encode(), corresponding to the second option in the previous list, to encode URI component values after URI variables are expanded.
  • NONE: No encoding is applied.

RestTemplate is set to EncodingMode.URI_COMPONENT for backward compatibility. WebClient depends on the DefaultUriBuilderFactory default value. This changed from EncodingMode.URI_COMPONENT in 5.0.x to EncodingMode.TEMPLATE_AND_VALUES in 5.1.