diff --git a/src/docs/asciidoc/integration.adoc b/src/docs/asciidoc/integration.adoc index 1951a56be3e6..59fad9db97d5 100644 --- a/src/docs/asciidoc/integration.adoc +++ b/src/docs/asciidoc/integration.adoc @@ -1139,12 +1139,9 @@ other method arguments. [[rest-resttemplate-uri]] ===== Working with the URI -For each of the main HTTP methods, the `RestTemplate` provides variants that either take -a String URI or `java.net.URI` as the first argument. - -The String URI variants accept template arguments as a String variable-length argument -or as a `Map`. They also assume the URL String is not encoded and needs -to be encoded. For example the following: +For each of the main HTTP methods, the `RestTemplate` provides two variants that take +either a String URI template, or `java.net.URI` as the first argument. When using a +String URI template, encoding is automatically applied: [source,java,indent=0] [subs="verbatim,quotes"] @@ -1152,39 +1149,11 @@ to be encoded. For example the following: 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 -String is already encoded, it will be encoded twice -- i.e. -`http://example.com/hotel%20list` will become `http://example.com/hotel%2520list`. If -this is not the intended effect, use the `java.net.URI` method variant, which assumes -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: +The resulting target URI is "http://example.com/hotel%20list". Alternatively you can +provide an already prepared `java.net.URI` and that will be used as is. +For more information on preparing URIs, or customizing how the `RestTemplate` expands +URI templates, see <> in the "Web Servlet" section. -[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]] ===== Dealing with request and response headers diff --git a/src/docs/asciidoc/web/web-uris.adoc b/src/docs/asciidoc/web/web-uris.adoc new file mode 100644 index 000000000000..5f3059887cbb --- /dev/null +++ b/src/docs/asciidoc/web/web-uris.adoc @@ -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 + +<> 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 <> 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. + diff --git a/src/docs/asciidoc/web/webmvc.adoc b/src/docs/asciidoc/web/webmvc.adoc index dec160b7bd18..5dcb3f50d8d7 100644 --- a/src/docs/asciidoc/web/webmvc.adoc +++ b/src/docs/asciidoc/web/webmvc.adoc @@ -2881,44 +2881,25 @@ Javadoc for more details. [[mvc-uri-building]] == URI Links -Spring MVC provides a mechanism for building and encoding a URI using -`UriComponentsBuilder` and `UriComponents`. +This section describes various options available in the Spring Framework to prepare URIs. -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] -[subs="verbatim,quotes"] ----- - UriComponents uriComponents = UriComponentsBuilder.newInstance() - .scheme("http").host("example.com").path("/hotels/{hotel}/bookings/{booking}").build() - .expand("42", "21") - .encode(); ----- +[[mvc-servleturicomponentsbuilder]] +=== Servlet request relative -In a Servlet environment the `ServletUriComponentsBuilder` subclass provides static -factory methods to copy available URL information from a Servlet requests: +You can use `ServletUriComponentsBuilder` to create URIs relative to the current request: [source,java,indent=0] [subs="verbatim,quotes"] ---- HttpServletRequest request = ... - // Re-use host, scheme, port, path and query string - // Replace the "accountId" query param + // Re-uses host, scheme, port, path and query string... ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromRequest(request) .replaceQueryParam("accountId", "{id}").build() @@ -2926,48 +2907,44 @@ factory methods to copy available URL information from a Servlet requests: .encode(); ---- -Alternatively, you may choose to copy a subset of the available information up to and -including the context path: +You can create URIs relative to the context path: [source,java,indent=0] [subs="verbatim,quotes"] ---- - // Re-use host, port and context path - // Append "/accounts" to the path + // Re-uses host, port and context path... ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromContextPath(request) .path("/accounts").build() ---- -Or in cases where the `DispatcherServlet` is mapped by name (e.g. `/main/{asterisk}`), you can -also have the literal part of the servlet mapping included: +You can create URIs relative to a Servlet (e.g. `/main/{asterisk}`): [source,java,indent=0] [subs="verbatim,quotes"] ---- - // Re-use host, port, context path - // Append the literal part of the servlet mapping to the path - // Append "/accounts" to the path + // Re-uses host, port, context path, and Servlet prefix... ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromServletMapping(request) .path("/accounts").build() ---- -[TIP] +[CAUTION] ==== -Both `ServletUriComponentsBuilder` and `MvcUriComponentsBuilder` detect, extract, and use -information from the "Forwarded" header, or from "X-Forwarded-Host", "X-Forwarded-Port", -and "X-Forwarded-Proto" if "Forwarded" is not present, so that the resulting links reflect -the original request. Note that you can also use the -<> to the same once, globally. +`ServletUriComponentsBuilder` 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 <> which processes such headers once +per request, and also provides an option to remove and ignore such headers. ==== [[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] [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(); ---- +[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 <> which processes such headers once +per request, and also provides an option to remove and ignore such headers. +==== + + [[mvc-links-to-controllers-from-views]]