UriComponents

Spring MVC and Spring WebFlux

UriComponentsBuilder helps create URIs from URIs-templates with variables, as shown in the following example:

Java

UriComponents uriComponents = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel }") 
        .queryParam("q", "{q}") 
        .encode() 
        .build(); 
URI uri = uriComponents.expand("Westin", "123").toUri(); 
  1. Static factory method with a URI template.
  2. Add or replace URI components.
  3. Request to encode a URI template and URI variables.
  4. Collect UriComponents.
  5. Expand the variables and get URI.
Kotlin

val uriComponents = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}") 
        .queryParam(" q", "{q}") 
        .encode() 
        .build() 
val uri = uriComponents.expand("Westin", "123").toUri() 
  1. Static factory method with a URI template.
  2. Add or replace URI components.
  3. Request to encode a URI template and URI variables.
  4. Collect UriComponents.
  5. We expand the variables and get URI.

The code from the previous example can be combined into one chain and shortened using 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()

You can shorten it even further by going directly to the URI (which implies an encoding), 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}")
        .queryParam("q", "{q}")
        .build("Westin", "123")

You can shorten it even further using a full URI template, as shown in the following example:

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

Spring MVC and Spring WebFlux

UriComponentsBuilder implements UriBuilder. In turn, you can create a UriBuilder using UriBuilderFactory. Together, UriBuilderFactory and UriBuilder provide a plug-in mechanism for generating URIs from URI templates based on common configuration, such as the base URL, encoding options, and other details.

You can configure RestTemplate and WebClient using UriBuilderFactory to configure URI preparation. DefaultUriBuilderFactory is a default implementation of UriBuilderFactory that uses UriComponentsBuilder internally and exposes general configuration options.

In the following The example shows how to configure such a bean:

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

Alternatively, you can use DefaultUriBuilderFactory directly. This is similar to using UriComponentsBuilder, but instead of static factory methods, it is an actual instance that stores configuration and parameters, as shown in the following example:

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

Encoding URIs

Spring MVC and Spring WebFlux

UriComponentsBuilder opens encoding options at two levels:

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

Consider ";" which is valid in a path, but has a reserved value. The first option replaces ";" to "%3B" in URI variables, but not in the URI template. In contrast, the second option never replaces ";" because it is a valid character in the path.

In most cases, the first option will most likely produce the expected result because it takes URI identifier variables into account as opaque data that must be fully encoded, while the second option is useful if URI variables intentionally contain reserved characters. The second option also works if you don't expand URI variables at all, because then anything that happens to look like a URI variable is encoded.

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 "/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 "/hotel%20list/New%20York?q=foo%2Bbar"

You can shorten the code in the previous example by going directly to A URI (which implies an encoding), as shown in the following example:

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 further by using a full URI wildcard, 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 expand and encode URI patterns internally using the UriBuilderFactory strategy. Both options can be configured using a custom strategy, as shown in the following example:

Java

String baseUrl = "https://example.com";
DefaultUriBuilderFactory
factory = new DefaultUriBuilderFactory(baseUrl)
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
// Setting up RestTemplate...
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(factory);
// Configure WebClient...
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
Kotlin

val baseUrl = "https://example.com"
val factory = DefaultUriBuilderFactory(baseUrl).apply {
    encodingMode = EncodingMode.TEMPLATE_AND_VALUES
}
// Setting up the RestTemplate...
val restTemplate = RestTemplate().apply {
    uriTemplateHandler = factory
}
// Configure the WebClient...
val client = WebClient.builder().uriBuilderFactory(factory).build()

Implementation DefaultUriBuilderFactory uses UriComponentsBuilder internally to expand and encode URI patterns. As a factory, it provides a single place to configure an 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 expanding.

  • VALUES_ONLY: Does not encode the URI template and instead applies strict encoding to URI variables via UriUtils#encodeUriVariables before expanding them into the template.

  • URI_COMPONENT: Uses UriComponents#encode(), corresponding to the second option in the previous list, to encode the component value of the URI after URI variable extensions.

  • NONE: No encoding is applied.

RestTemplate is set to EncodingMode.URI_COMPONENT for historical reasons and for backward compatibility. WebClient accesses the default value in DefaultUriBuilderFactory, which was changed from EncodingMode.URI_COMPONENT in 5.0.x to EncodingMode.TEMPLATE_AND_VALUES in 5.1.

Servlet Relative Requests

You can use the ServletUriComponentsBuilder to create URIs relative to the current request, as shown in the following example:

Java

HttpServletRequest request = ...
// Reuses scheme, host, port, path and query string...
URI uri = ServletUriComponentsBuilder.fromRequest(request)
        .replaceQueryParam("accountId", "{id}")
        .build("123");
Kotlin

val request: HttpServletRequest = ...
// Reuses scheme, host, port, path and query string...
val uri = ServletUriComponentsBuilder.fromRequest(request)
        .replaceQueryParam("accountId", "{id}")
        .build ("123")

You can create URIs relative to the context path, as shown in the following example:

Java

HttpServletRequest request = ...
// Reuse scheme, host, port and context path...
URI uri = ServletUriComponentsBuilder.fromContextPath(request)
        .path("/accounts")
        .build()
        .toUri();
Kotlin

val request: HttpServletRequest = ...
// Reuse the schema, host, port and context path...
val uri = ServletUriComponentsBuilder.fromContextPath(request)
        .path("/accounts")
        .build()
        .toUri()

You can create URIs relative to the servlet (for example, /main/*), as shown in the following example:

Java

HttpServletRequest request = ...
// Reuses the scheme, host, port, context path and servlet mapping prefix...
URI uri = ServletUriComponentsBuilder.fromServletMapping(request)
        .path("/accounts")
        .build()
        .toUri();
Kotlin

val request: HttpServletRequest = ...
// Reuses scheme, host, port , context path and servlet mapping prefix...
val uri = ServletUriComponentsBuilder.fromServletMapping(request)
        .path("/accounts")
        .build()
        .toUri()
Starting with version 5.1, ServletUriComponentsBuilder ignores information from the Forwarded and X-Forwarded-* headers, which indicate the client-side address. Consider using ForwardedHeaderFilter to retrieve and use or discard such headers.

Controller References

Spring MVC provides a mechanism for preparing references to controller methods. For example, the following MVC controller allows you to create links:

Java

@Controller
@RequestMapping("/hotels/{hotel}")
public class BookingController {
    @GetMapping(" /bookings/{booking}")
    public ModelAndView getBooking(@PathVariable Long booking) {
        // ...
    }
}
Kotlin

@Controller
@RequestMapping("/hotels/{hotel}")
class BookingController {
    @GetMapping("/bookings/{booking}")
    fun getBooking(@PathVariable booking: Long): ModelAndView {
        // ...
    }
}

You can prepare a reference by accessing the method by name, as shown in the following example:

Java

UriComponents uriComponents = MvcUriComponentsBuilder
    .fromMethodName(BookingController.class, "getBooking", 21).buildAndExpand(42);
URI uri = uriComponents.encode().toUri();
Kotlin

val uriComponents = MvcUriComponentsBuilder
    .fromMethodName (BookingController::class.java, "getBooking", 21).buildAndExpand(42)
val uri = uriComponents.encode().toUri()

In the previous example, we specify the actual values of the method arguments (in this case, the long value: 21) to be used as a path variable and inserted into the URL. Additionally, we specify the value 42 to populate any remaining URI variables, such as the hotel variable inherited from the type-level query mapping. If the method had more arguments, we could supply null for arguments not needed for the URL. In general, only arguments with the @PathVariable and @RequestParam annotations are meaningful for URL construction.

There are additional ways to use the MvcUriComponentsBuilder. For example, to avoid calling a controller method by name, as shown in the following example (the example assumes a static import of MvcUriComponentsBuilder.on), you can use a technique similar to testing using mock objects through a proxy:

Java

UriComponents uriComponents = MvcUriComponentsBuilder
    .fromMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42);
URI uri = uriComponents.encode().toUri();
Kotlin

val uriComponents = MvcUriComponentsBuilder
    .fromMethodCall (on(BookingController::class.java).getBooking(21)).buildAndExpand(42)
val uri = uriComponents.encode().toUri()
Controller method signatures are limited in their design if they are intended to be used to create references using fromMethodCall. In addition to the need for the correct parameter signature, there is a technical limitation on the return type (namely the creation of a run-time proxy for linker calls), so the return type must not be final. In particular, the normal String return type for view names doesn't work here. Instead, you should use ModelAndView or even a simple Object (with a String return value).

The previous examples use static methods in MvcUriComponentsBuilder. Internally, they call the ServletUriComponentsBuilder to prepare a base URL from the scheme, host, port, context path, and servlet path of the current request. This works great in most cases. However, sometimes this may not be enough. For example, you may be outside the context of the request (for example, in a batch process that prepares links), or you may need to insert a path prefix (for example, a locale prefix that was removed from the request path and needs to be inserted back into the links) .

For such cases, you can use static overloads of fromXxx methods that take a UriComponentsBuilder to use the base URL. Alternatively, you can instantiate MvcUriComponentsBuilder with a base URL, and then use withXxx methods based on the instance. For example, the following listing uses withMethodCall:

Java

UriComponentsBuilder base = ServletUriComponentsBuilder.fromCurrentContextPath().path("/en");
MvcUriComponentsBuilder builder = MvcUriComponentsBuilder.relativeTo(base);
builder.withMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42);
URI uri = uriComponents.encode().toUri();
Kotlin

val base = ServletUriComponentsBuilder.fromCurrentContextPath().path("/en")
val builder = MvcUriComponentsBuilder.relativeTo(base)
builder.withMethodCall(on(BookingController::class.java).getBooking(21)).buildAndExpand(42)
val uri = uriComponents.encode().toUri()
Since version 5.1, MvcUriComponentsBuilder ignores information from the Forwarded and X-Forwarded-* headers, which indicate the client-side address. Consider using a ForwardedHeaderFilter to retrieve and use or discard such headers.

References in Views

In views such as Thymeleaf, FreeMarker, or JSP, you can form references to annotated controllers by referencing an implicitly or explicitly assigned name for each query mapping.

Consider the following example:

Java

@RequestMapping("/people/{id}/addresses")
public class PersonAddressController {
    @RequestMapping("/{country}")
    public HttpEntity<PersonAddress> getAddress(@PathVariable String country) { ... }
}
Kotlin

@RequestMapping("/people/{id}/addresses")
class PersonAddressController {
    @RequestMapping("/{country}")
    fun getAddress(@PathVariable country: String): HttpEntity<PersonAddress> { ... }
}

Given the previous controller, you can prepare a link from JSP in the following form:


<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
...
<a href="${s:mvcUrl('PAC#getAddress').arg(0,'US').buildAndExpand('123')}">Get Address</a>

In the previous example, we rely on the mvcUrl function declared in the Spring tag library (that is, META-INF/spring.tld), but Defining your own function or preparing a similar one for other templating technologies is also quite simple.

Here's how it works. When you run each @RequestMapping annotation, it is assigned a default name via the HandlerMethodMappingNamingStrategy strategy, the default implementation of which capitalizes the class and method name (for example, the getThing method in ThingController becomes "TC#getThing"). If the names do not match, you can use @RequestMapping(name="..") to assign an explicit name or implement your own naming strategy HandlerMethodMappingNamingStrategy.