Up-to-date and expanded coverage on preparing URIs
Issue: SPR-16422
This commit is contained in:
parent
9801afb85d
commit
aa4bcedad3
|
@ -1139,12 +1139,9 @@ other method arguments.
|
||||||
[[rest-resttemplate-uri]]
|
[[rest-resttemplate-uri]]
|
||||||
===== Working with the URI
|
===== Working with the URI
|
||||||
|
|
||||||
For each of the main HTTP methods, the `RestTemplate` provides variants that either take
|
For each of the main HTTP methods, the `RestTemplate` provides two variants that take
|
||||||
a String URI or `java.net.URI` as the first argument.
|
either a String URI template, or `java.net.URI` as the first argument. When using a
|
||||||
|
String URI template, encoding is automatically applied:
|
||||||
The String URI variants accept template arguments as a String variable-length argument
|
|
||||||
or as a `Map<String,String>`. They also assume the URL String is not encoded and needs
|
|
||||||
to be encoded. For example the following:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0]
|
||||||
[subs="verbatim,quotes"]
|
[subs="verbatim,quotes"]
|
||||||
|
@ -1152,39 +1149,11 @@ to be encoded. For example the following:
|
||||||
restTemplate.getForObject("http://example.com/hotel list", String.class);
|
restTemplate.getForObject("http://example.com/hotel list", String.class);
|
||||||
----
|
----
|
||||||
|
|
||||||
will perform a GET on `http://example.com/hotel%20list`. That means if the input URL
|
The resulting target URI is "http://example.com/hotel%20list". Alternatively you can
|
||||||
String is already encoded, it will be encoded twice -- i.e.
|
provide an already prepared `java.net.URI` and that will be used as is.
|
||||||
`http://example.com/hotel%20list` will become `http://example.com/hotel%2520list`. If
|
For more information on preparing URIs, or customizing how the `RestTemplate` expands
|
||||||
this is not the intended effect, use the `java.net.URI` method variant, which assumes
|
URI templates, see <<web.adoc#mvc-uri-building,URI Links>> in the "Web Servlet" section.
|
||||||
the URL is already encoded is also generally useful if you want to reuse a single (fully
|
|
||||||
expanded) `URI` multiple times.
|
|
||||||
|
|
||||||
The `UriComponentsBuilder` class can be used to build and encode the `URI` including
|
|
||||||
support for URI templates. For example you can start with a URL String:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(
|
|
||||||
"http://example.com/hotels/{hotel}/bookings/{booking}").build()
|
|
||||||
.expand("42", "21")
|
|
||||||
.encode();
|
|
||||||
|
|
||||||
URI uri = uriComponents.toUri();
|
|
||||||
----
|
|
||||||
|
|
||||||
Or specify each URI component individually:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
UriComponents uriComponents = UriComponentsBuilder.newInstance()
|
|
||||||
.scheme("http").host("example.com").path("/hotels/{hotel}/bookings/{booking}").build()
|
|
||||||
.expand("42", "21")
|
|
||||||
.encode();
|
|
||||||
|
|
||||||
URI uri = uriComponents.toUri();
|
|
||||||
----
|
|
||||||
|
|
||||||
[[rest-template-headers]]
|
[[rest-template-headers]]
|
||||||
===== Dealing with request and response headers
|
===== Dealing with request and response headers
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
|
||||||
|
[[web-uricomponents]]
|
||||||
|
= UriComponents
|
||||||
|
|
||||||
|
`UriComponents` is comparable to `java.net.URI`. However it comes with a dedicated
|
||||||
|
`UriComponentsBuilder` and support URI template variables:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
[subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
String uriTemplate = "http://example.com/hotels/{hotel}";
|
||||||
|
|
||||||
|
UriComponents uriComponents = UriComponentsBuilder.fromUriString(uriTemplate) // <1>
|
||||||
|
.queryParam("q", "{q}") // <2>
|
||||||
|
.build(); // <3>
|
||||||
|
|
||||||
|
URI uri = uriComponents.expand("Westin", "123").encode().toUri(); // <4>
|
||||||
|
----
|
||||||
|
<1> Static factory method with a URI template.
|
||||||
|
<2> Add or replace URI components.
|
||||||
|
<3> Build `UriComponents`.
|
||||||
|
<4> Expand URI variables, encode, and obtain the `URI`.
|
||||||
|
|
||||||
|
The above can be done as a single chain, and with a shortcut:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
[subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
String uriTemplate = "http://example.com/hotels/{hotel}";
|
||||||
|
|
||||||
|
URI uri = UriComponentsBuilder.fromUriString(uriTemplate)
|
||||||
|
.queryParam("q", "{q}")
|
||||||
|
.buildAndExpand("Westin", "123")
|
||||||
|
.encode()
|
||||||
|
.toUri();
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[web-uribuilder]]
|
||||||
|
= UriBuilder
|
||||||
|
|
||||||
|
<<web-uricomponents,UriComponentsBuilder>> is an implementation of `UriBuilder`. Together
|
||||||
|
`UriBuilderFactory` and `UriBuilder` provide a pluggable mechanism for building a URI
|
||||||
|
from a URI template, as well as a way to share common properties such as a base URI,
|
||||||
|
encoding strategy, and others.
|
||||||
|
|
||||||
|
Both the `RestTemplate` and the `WebClient` can be configured with a `UriBuilderFactory`,
|
||||||
|
in order to customize how URIs are created from URI templates. The default implementation
|
||||||
|
relies on `UriComponentsBuilder` internally and provides options to a common base URI,
|
||||||
|
an alternative encoding mode strategy, and more.
|
||||||
|
|
||||||
|
An example of configuring the `RestTemplate`:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
[subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
String baseUrl = "http://example.com";
|
||||||
|
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
|
||||||
|
|
||||||
|
RestTemplate restTemplate = new RestTemplate();
|
||||||
|
restTemplate.setUriTemplateHandler(factory);
|
||||||
|
----
|
||||||
|
|
||||||
|
Examples of configuring the `WebClient`:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
[subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
String baseUrl = "http://example.com";
|
||||||
|
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
|
||||||
|
|
||||||
|
// Configure the UriBuilderFactory..
|
||||||
|
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
|
||||||
|
|
||||||
|
// Or use shortcut on builder..
|
||||||
|
WebClient client = WebClient.builder().baseUrl(baseUrl).build();
|
||||||
|
|
||||||
|
// Or use create shortcut...
|
||||||
|
WebClient client = WebClient.create(baseUrl);
|
||||||
|
----
|
||||||
|
|
||||||
|
You can also use `DefaultUriBuilderFactory` directly, as you would `UriComponentsBuilder`.
|
||||||
|
The main difference is that `DefaultUriBuilderFactory` is stateful and can be re-used to
|
||||||
|
prepare many URLs, sharing common configuration, such as a base URL, while
|
||||||
|
`UriComponentsBuilder` is stateless and per URI.
|
||||||
|
|
||||||
|
An example of using the `DefaultUriBuilderFactory`:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
[subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
String baseUrl = "http://example.com";
|
||||||
|
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);
|
||||||
|
|
||||||
|
URI uri = uriBuilderFactory.uriString("/hotels/{hotel}")
|
||||||
|
.queryParam("q", "{q}")
|
||||||
|
.build("Westin", "123"); // encoding strategy applied..
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[web-uri-encoding]]
|
||||||
|
= URI Encoding
|
||||||
|
|
||||||
|
The default way of encoding URIs in `UriComponents` works as follows:
|
||||||
|
|
||||||
|
. URI variables are expanded.
|
||||||
|
. URI components are encoded individually.
|
||||||
|
|
||||||
|
For each URI component, percent encoding is applied to all illegal characters, which
|
||||||
|
includes non-US-ASCII characters, and other characters that are illegal within a given
|
||||||
|
URI component type, as defined in RFC 3986.
|
||||||
|
|
||||||
|
[TIP]
|
||||||
|
====
|
||||||
|
The encoding in `UriComponents` is comparable to the multi-argument constructor of
|
||||||
|
`java.net.URI`, as described in the "Escaped octets, quotation, encoding, and decoding"
|
||||||
|
section of its class-level Javadoc.
|
||||||
|
====
|
||||||
|
|
||||||
|
This default way of encoding *does not* encode all characters with reserved meaning, but
|
||||||
|
only the ones that are illegal within a given URI component. If this is not what you
|
||||||
|
expect you can use an alternative.
|
||||||
|
|
||||||
|
When using <<web-uribuilder,DefaultUriBuilderFactory>> you can switch to an alternative
|
||||||
|
encoding strategy:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
[subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
String baseUrl = "http://example.com";
|
||||||
|
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl)
|
||||||
|
factory.setEncodingMode(EncodingMode.VALUES_ONLY);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
----
|
||||||
|
|
||||||
|
The above encoding strategy applies `UriUtils.encode(String, Charset)` to each URI
|
||||||
|
variable value prior to expanding it. Effectively it encodes all characters with reserved
|
||||||
|
meaning, therefore ensuring that expanded URI variable do not have any impact on the
|
||||||
|
structure or meaning of the URI.
|
||||||
|
|
|
@ -2881,44 +2881,25 @@ Javadoc for more details.
|
||||||
[[mvc-uri-building]]
|
[[mvc-uri-building]]
|
||||||
== URI Links
|
== URI Links
|
||||||
|
|
||||||
Spring MVC provides a mechanism for building and encoding a URI using
|
This section describes various options available in the Spring Framework to prepare URIs.
|
||||||
`UriComponentsBuilder` and `UriComponents`.
|
|
||||||
|
|
||||||
For example you can expand and encode a URI template string:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(
|
|
||||||
"http://example.com/hotels/{hotel}/bookings/{booking}").build();
|
|
||||||
|
|
||||||
URI uri = uriComponents.expand("42", "21").encode().toUri();
|
include::web-uris.adoc[leveloffset=+2]
|
||||||
----
|
|
||||||
|
|
||||||
Note that `UriComponents` is immutable and the `expand()` and `encode()` operations
|
|
||||||
return new instances if necessary.
|
|
||||||
|
|
||||||
You can also expand and encode using individual URI components:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
[[mvc-servleturicomponentsbuilder]]
|
||||||
[subs="verbatim,quotes"]
|
=== Servlet request relative
|
||||||
----
|
|
||||||
UriComponents uriComponents = UriComponentsBuilder.newInstance()
|
|
||||||
.scheme("http").host("example.com").path("/hotels/{hotel}/bookings/{booking}").build()
|
|
||||||
.expand("42", "21")
|
|
||||||
.encode();
|
|
||||||
----
|
|
||||||
|
|
||||||
In a Servlet environment the `ServletUriComponentsBuilder` subclass provides static
|
You can use `ServletUriComponentsBuilder` to create URIs relative to the current request:
|
||||||
factory methods to copy available URL information from a Servlet requests:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0]
|
||||||
[subs="verbatim,quotes"]
|
[subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
HttpServletRequest request = ...
|
HttpServletRequest request = ...
|
||||||
|
|
||||||
// Re-use host, scheme, port, path and query string
|
// Re-uses host, scheme, port, path and query string...
|
||||||
// Replace the "accountId" query param
|
|
||||||
|
|
||||||
ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromRequest(request)
|
ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromRequest(request)
|
||||||
.replaceQueryParam("accountId", "{id}").build()
|
.replaceQueryParam("accountId", "{id}").build()
|
||||||
|
@ -2926,48 +2907,44 @@ factory methods to copy available URL information from a Servlet requests:
|
||||||
.encode();
|
.encode();
|
||||||
----
|
----
|
||||||
|
|
||||||
Alternatively, you may choose to copy a subset of the available information up to and
|
You can create URIs relative to the context path:
|
||||||
including the context path:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0]
|
||||||
[subs="verbatim,quotes"]
|
[subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
// Re-use host, port and context path
|
// Re-uses host, port and context path...
|
||||||
// Append "/accounts" to the path
|
|
||||||
|
|
||||||
ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromContextPath(request)
|
ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromContextPath(request)
|
||||||
.path("/accounts").build()
|
.path("/accounts").build()
|
||||||
----
|
----
|
||||||
|
|
||||||
Or in cases where the `DispatcherServlet` is mapped by name (e.g. `/main/{asterisk}`), you can
|
You can create URIs relative to a Servlet (e.g. `/main/{asterisk}`):
|
||||||
also have the literal part of the servlet mapping included:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0]
|
||||||
[subs="verbatim,quotes"]
|
[subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
// Re-use host, port, context path
|
// Re-uses host, port, context path, and Servlet prefix...
|
||||||
// Append the literal part of the servlet mapping to the path
|
|
||||||
// Append "/accounts" to the path
|
|
||||||
|
|
||||||
ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromServletMapping(request)
|
ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromServletMapping(request)
|
||||||
.path("/accounts").build()
|
.path("/accounts").build()
|
||||||
----
|
----
|
||||||
|
|
||||||
[TIP]
|
[CAUTION]
|
||||||
====
|
====
|
||||||
Both `ServletUriComponentsBuilder` and `MvcUriComponentsBuilder` detect, extract, and use
|
`ServletUriComponentsBuilder` detects and uses information from the "Forwarded",
|
||||||
information from the "Forwarded" header, or from "X-Forwarded-Host", "X-Forwarded-Port",
|
"X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" headers, so the resulting
|
||||||
and "X-Forwarded-Proto" if "Forwarded" is not present, so that the resulting links reflect
|
links reflect the original request. You need to ensure that your application is behind
|
||||||
the original request. Note that you can also use the
|
a trusted proxy which filters out such headers coming from outside. Also consider using
|
||||||
<<filters-forwarded-headers,ForwardedHeaderFilter>> to the same once, globally.
|
the <<filters-forwarded-headers,ForwardedHeaderFilter>> which processes such headers once
|
||||||
|
per request, and also provides an option to remove and ignore such headers.
|
||||||
====
|
====
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[mvc-links-to-controllers]]
|
[[mvc-links-to-controllers]]
|
||||||
=== Links to Controllers
|
=== Links to controllers
|
||||||
|
|
||||||
Spring MVC also provides a mechanism for building links to controller methods. For example, given:
|
Spring MVC provides a mechanism to prepare links to controller methods. For example:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0]
|
||||||
[subs="verbatim,quotes"]
|
[subs="verbatim,quotes"]
|
||||||
|
@ -3036,6 +3013,17 @@ with a base URL and then use the instance-based "withXxx" methods. For example:
|
||||||
URI uri = uriComponents.encode().toUri();
|
URI uri = uriComponents.encode().toUri();
|
||||||
----
|
----
|
||||||
|
|
||||||
|
[CAUTION]
|
||||||
|
====
|
||||||
|
`MvcUriComponentsBuilder` detects and uses information from the "Forwarded",
|
||||||
|
"X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" headers, so the resulting
|
||||||
|
links reflect the original request. You need to ensure that your application is behind
|
||||||
|
a trusted proxy which filters out such headers coming from outside. Also consider using
|
||||||
|
the <<filters-forwarded-headers,ForwardedHeaderFilter>> which processes such headers once
|
||||||
|
per request, and also provides an option to remove and ignore such headers.
|
||||||
|
====
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[mvc-links-to-controllers-from-views]]
|
[[mvc-links-to-controllers-from-views]]
|
||||||
|
|
Loading…
Reference in New Issue