424 lines
12 KiB
Plaintext
424 lines
12 KiB
Plaintext
[[uricomponents]]
|
|
= UriComponents
|
|
[.small]#Spring MVC and Spring WebFlux#
|
|
|
|
`UriComponentsBuilder` helps to build URI's from URI templates with variables, as the following example shows:
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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> Static factory method with a URI template.
|
|
<2> Add or replace URI components.
|
|
<3> Request to have the URI template and URI variables encoded.
|
|
<4> Build a `UriComponents`.
|
|
<5> Expand variables and obtain the `URI`.
|
|
|
|
Kotlin::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
val uriComponents = UriComponentsBuilder
|
|
.fromUriString("https://example.com/hotels/{hotel}") // <1>
|
|
.queryParam("q", "{q}") // <2>
|
|
.encode() // <3>
|
|
.build() // <4>
|
|
|
|
val uri = uriComponents.expand("Westin", "123").toUri() // <5>
|
|
----
|
|
<1> Static factory method with a URI template.
|
|
<2> Add or replace URI components.
|
|
<3> Request to have the URI template and URI variables encoded.
|
|
<4> Build a `UriComponents`.
|
|
<5> Expand variables and obtain the `URI`.
|
|
======
|
|
|
|
The preceding example can be consolidated into one chain and shortened with `buildAndExpand`,
|
|
as the following example shows:
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
URI uri = UriComponentsBuilder
|
|
.fromUriString("https://example.com/hotels/{hotel}")
|
|
.queryParam("q", "{q}")
|
|
.encode()
|
|
.buildAndExpand("Westin", "123")
|
|
.toUri();
|
|
----
|
|
|
|
Kotlin::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
val uri = UriComponentsBuilder
|
|
.fromUriString("https://example.com/hotels/{hotel}")
|
|
.queryParam("q", "{q}")
|
|
.encode()
|
|
.buildAndExpand("Westin", "123")
|
|
.toUri()
|
|
----
|
|
======
|
|
|
|
You can shorten it further by going directly to a URI (which implies encoding),
|
|
as the following example shows:
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
URI uri = UriComponentsBuilder
|
|
.fromUriString("https://example.com/hotels/{hotel}")
|
|
.queryParam("q", "{q}")
|
|
.build("Westin", "123");
|
|
----
|
|
|
|
Kotlin::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
val uri = UriComponentsBuilder
|
|
.fromUriString("https://example.com/hotels/{hotel}")
|
|
.queryParam("q", "{q}")
|
|
.build("Westin", "123")
|
|
----
|
|
======
|
|
|
|
You can shorten it further still with a full URI template, as the following example shows:
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
URI uri = UriComponentsBuilder
|
|
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
|
|
.build("Westin", "123");
|
|
----
|
|
|
|
Kotlin::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
val uri = UriComponentsBuilder
|
|
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
|
|
.build("Westin", "123")
|
|
----
|
|
======
|
|
|
|
|
|
|
|
[[uribuilder]]
|
|
= UriBuilder
|
|
[.small]#Spring MVC and Spring WebFlux#
|
|
|
|
<<web-uricomponents, `UriComponentsBuilder`>> implements `UriBuilder`. You can create a
|
|
`UriBuilder`, in turn, with a `UriBuilderFactory`. Together, `UriBuilderFactory` and
|
|
`UriBuilder` provide a pluggable mechanism to build URIs from URI templates, based on
|
|
shared configuration, such as a base URL, encoding preferences, and other details.
|
|
|
|
You can configure `RestTemplate` and `WebClient` with a `UriBuilderFactory`
|
|
to customize the preparation of URIs. `DefaultUriBuilderFactory` is a default
|
|
implementation of `UriBuilderFactory` that uses `UriComponentsBuilder` internally and
|
|
exposes shared configuration options.
|
|
|
|
The following example shows how to configure a `RestTemplate`:
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
// 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::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
// 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 a `WebClient`:
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
// 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::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
// 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()
|
|
----
|
|
======
|
|
|
|
In addition, you can also use `DefaultUriBuilderFactory` directly. It is similar to using
|
|
`UriComponentsBuilder` but, instead of static factory methods, it is an actual instance
|
|
that holds configuration and preferences, as the following example shows:
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
String baseUrl = "https://example.com";
|
|
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);
|
|
|
|
URI uri = uriBuilderFactory.uriString("/hotels/{hotel}")
|
|
.queryParam("q", "{q}")
|
|
.build("Westin", "123");
|
|
----
|
|
|
|
Kotlin::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
val baseUrl = "https://example.com"
|
|
val uriBuilderFactory = DefaultUriBuilderFactory(baseUrl)
|
|
|
|
val uri = uriBuilderFactory.uriString("/hotels/{hotel}")
|
|
.queryParam("q", "{q}")
|
|
.build("Westin", "123")
|
|
----
|
|
======
|
|
|
|
|
|
[[uri-parsing]]
|
|
= URI Parsing
|
|
[.small]#Spring MVC and Spring WebFlux#
|
|
|
|
`UriComponentsBuilder` supports two URI parser types:
|
|
|
|
1. RFC parser -- this parser type expects URI strings to conform to RFC 3986 syntax,
|
|
and treats deviations from the syntax as illegal.
|
|
|
|
2. WhatWG parser -- this parser is based on the
|
|
https://github.com/web-platform-tests/wpt/tree/master/url[URL parsing algorithm] in the
|
|
https://url.spec.whatwg.org[WhatWG URL Living standard]. It provides lenient handling of
|
|
a wide range of cases of unexpected input. Browsers implement this in order to handle
|
|
leniently user typed URL's. For more details, see the URL Living Standard and URL parsing
|
|
https://github.com/web-platform-tests/wpt/tree/master/url[test cases].
|
|
|
|
By default, `RestClient`, `WebClient`, and `RestTemplate` use the RFC parser type, and
|
|
expect applications to provide with URL templates that conform to RFC syntax. To change
|
|
that you can customize the `UriBuilderFactory` on any of the clients.
|
|
|
|
Applications and frameworks may further rely on `UriComponentsBuilder` for their own needs
|
|
to parse user provided URL's in order to inspect and possibly validated URI components
|
|
such as the scheme, host, port, path, and query. Such components can decide to use the
|
|
WhatWG parser type in order to handle URL's more leniently, and to align with the way
|
|
browsers parse URI's, in case of a redirect to the input URL or if it is included in a
|
|
response to a browser.
|
|
|
|
|
|
[[uri-encoding]]
|
|
= URI Encoding
|
|
[.small]#Spring MVC and Spring WebFlux#
|
|
|
|
`UriComponentsBuilder` exposes encoding options at two levels:
|
|
|
|
* {spring-framework-api}/web/util/UriComponentsBuilder.html#encode--[UriComponentsBuilder#encode()]:
|
|
Pre-encodes the URI template first and then strictly encodes URI variables when expanded.
|
|
* {spring-framework-api}/web/util/UriComponents.html#encode--[UriComponents#encode()]:
|
|
Encodes URI components _after_ URI variables are expanded.
|
|
|
|
Both options replace non-ASCII and illegal characters with escaped octets. However, the first option
|
|
also replaces characters with reserved meaning that appear in URI variables.
|
|
|
|
TIP: Consider ";", which is legal in a path but has reserved meaning. The first option replaces
|
|
";" with "%3B" in URI variables but not in the URI template. By contrast, the second option never
|
|
replaces ";", since it is a legal character in a path.
|
|
|
|
For most cases, the first option is likely to give the expected result, because it treats URI
|
|
variables as opaque data to be fully encoded, while the second option is useful if URI
|
|
variables do intentionally contain reserved characters. The second option is also useful
|
|
when not expanding URI variables at all since that will also encode anything that
|
|
incidentally looks like a URI variable.
|
|
|
|
The following example uses the first option:
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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"
|
|
----
|
|
======
|
|
|
|
You can shorten the preceding example by going directly to the URI (which implies encoding),
|
|
as the following example shows:
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
|
|
.queryParam("q", "{q}")
|
|
.build("New York", "foo+bar");
|
|
----
|
|
|
|
Kotlin::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
|
|
.queryParam("q", "{q}")
|
|
.build("New York", "foo+bar")
|
|
----
|
|
======
|
|
|
|
You can shorten it further still with a full URI template, as the following example shows:
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
URI uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
|
|
.build("New York", "foo+bar");
|
|
----
|
|
|
|
Kotlin::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
val uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
|
|
.build("New York", "foo+bar")
|
|
----
|
|
======
|
|
|
|
The `WebClient` and the `RestTemplate` expand and encode URI templates internally through
|
|
the `UriBuilderFactory` strategy. Both can be configured with a custom strategy,
|
|
as the following example shows:
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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 approach to encoding, based on one of the below encoding modes:
|
|
|
|
* `TEMPLATE_AND_VALUES`: Uses `UriComponentsBuilder#encode()`, corresponding to
|
|
the first option in the earlier list, to pre-encode the URI template and strictly encode URI variables when
|
|
expanded.
|
|
* `VALUES_ONLY`: Does not encode the URI template and, instead, applies strict encoding
|
|
to URI variables through `UriUtils#encodeUriVariables` prior to expanding them into the
|
|
template.
|
|
* `URI_COMPONENT`: Uses `UriComponents#encode()`, corresponding to the second option in the earlier list, to
|
|
encode URI component value _after_ URI variables are expanded.
|
|
* `NONE`: No encoding is applied.
|
|
|
|
The `RestTemplate` is set to `EncodingMode.URI_COMPONENT` for historical
|
|
reasons and for backwards compatibility. The `WebClient` relies on the default value
|
|
in `DefaultUriBuilderFactory`, which was changed from `EncodingMode.URI_COMPONENT` in
|
|
5.0.x to `EncodingMode.TEMPLATE_AND_VALUES` in 5.1.
|