UriComponents
Spring MVC and Spring WebFlux
UriComponentsBuilder
helps create URIs
from URIs-templates with variables, as shown in the following example:
UriComponents uriComponents = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel }")
.queryParam("q", "{q}")
.encode()
.build();
URI uri = uriComponents.expand("Westin", "123").toUri();
- Static factory method with a URI template.
- Add or replace URI components.
- Request to encode a URI template and URI variables.
- Collect
UriComponents
. - Expand the variables and get
URI
.
val uriComponents = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam(" q", "{q}")
.encode()
.build()
val uri = uriComponents.expand("Westin", "123").toUri()
- Static factory method with a URI template.
- Add or replace URI components.
- Request to encode a URI template and URI variables.
- Collect
UriComponents
. - 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:
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()
You can shorten it even further by going directly to the URI (which implies an encoding), as shown in the following example:
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")
You can shorten it even further using a full URI template, as shown in the following example:
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
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:
// 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
The following example configures the 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()
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:
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")
Encoding URIs
Spring MVC and Spring WebFlux
UriComponentsBuilder
opens encoding options at two levels:
UriComponentsBuilder#encode(): First pre-encodes the URI pattern, and then strictly encodes URI variables when expanded.
UriComponents#encode(): Encodes URI components after URI variable expansion.
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.
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:
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"
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:
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")
You can shorten it even further by using a full URI wildcard, as shown in the following example:
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
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:
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();
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
: UsesUriComponentsBuilder# 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 viaUriUtils#encodeUriVariables
before expanding them into the template.URI_COMPONENT
: UsesUriComponents#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:
HttpServletRequest request = ...
// Reuses scheme, host, port, path and query string...
URI uri = ServletUriComponentsBuilder.fromRequest(request)
.replaceQueryParam("accountId", "{id}")
.build("123");
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:
HttpServletRequest request = ...
// Reuse scheme, host, port and context path...
URI uri = ServletUriComponentsBuilder.fromContextPath(request)
.path("/accounts")
.build()
.toUri();
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:
HttpServletRequest request = ...
// Reuses the scheme, host, port, context path and servlet mapping prefix...
URI uri = ServletUriComponentsBuilder.fromServletMapping(request)
.path("/accounts")
.build()
.toUri();
val request: HttpServletRequest = ...
// Reuses scheme, host, port , context path and servlet mapping prefix...
val uri = ServletUriComponentsBuilder.fromServletMapping(request)
.path("/accounts")
.build()
.toUri()
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:
@Controller
@RequestMapping("/hotels/{hotel}")
public class BookingController {
@GetMapping(" /bookings/{booking}")
public ModelAndView getBooking(@PathVariable Long booking) {
// ...
}
}
@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:
UriComponents uriComponents = MvcUriComponentsBuilder
.fromMethodName(BookingController.class, "getBooking", 21).buildAndExpand(42);
URI uri = uriComponents.encode().toUri();
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:
UriComponents uriComponents = MvcUriComponentsBuilder
.fromMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42);
URI uri = uriComponents.encode().toUri();
val uriComponents = MvcUriComponentsBuilder
.fromMethodCall (on(BookingController::class.java).getBooking(21)).buildAndExpand(42)
val uri = uriComponents.encode().toUri()
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
:
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();
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()
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:
@RequestMapping("/people/{id}/addresses")
public class PersonAddressController {
@RequestMapping("/{country}")
public HttpEntity<PersonAddress> getAddress(@PathVariable String country) { ... }
}
@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
.
GO TO FULL VERSION