URI 連結

本節介紹 Spring Framework 中可用於準備 URI 的各種選項。

UriComponents

Spring MVC 和 Spring WebFlux

UriComponentsBuilder 協助從帶有變數的 URI 範本建立 URI,如下列範例所示

  • Java

  • Kotlin

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 具有 URI 範本的靜態工廠方法。
2 新增或取代 URI 組件。
3 請求對 URI 範本和 URI 變數進行編碼。
4 建置 UriComponents
5 展開變數並取得 URI
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 具有 URI 範本的靜態工廠方法。
2 新增或取代 URI 組件。
3 請求對 URI 範本和 URI 變數進行編碼。
4 建置 UriComponents
5 展開變數並取得 URI

前面的範例可以整合到一個鏈中,並使用 buildAndExpand 縮短,如下列範例所示

  • Java

  • Kotlin

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

您可以進一步縮短它,直接前往 URI(這意味著編碼),如下列範例所示

  • Java

  • Kotlin

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

您可以使用完整的 URI 範本進一步縮短它,如下列範例所示

  • Java

  • Kotlin

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 和 Spring WebFlux

UriComponentsBuilder 實作 UriBuilder。您可以反過來使用 UriBuilderFactory 建立 UriBuilderUriBuilderFactoryUriBuilder 一起提供一個可插拔的機制,用於從 URI 範本建立 URI,基於共享組態,例如基本 URL、編碼偏好和其他詳細資訊。

您可以組態 RestTemplateWebClientUriBuilderFactory,以自訂 URI 的準備方式。DefaultUriBuilderFactoryUriBuilderFactory 的預設實作,它在內部使用 UriComponentsBuilder 並公開共享組態選項。

以下範例顯示如何組態 RestTemplate

  • Java

  • Kotlin

// 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

以下範例組態 WebClient

  • Java

  • Kotlin

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

此外,您也可以直接使用 DefaultUriBuilderFactory。它類似於使用 UriComponentsBuilder,但它不是靜態工廠方法,而是一個實際的實例,它持有組態和偏好設定,如下列範例所示

  • Java

  • Kotlin

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

URI 解析

Spring MVC 和 Spring WebFlux

UriComponentsBuilder 支援兩種 URI 解析器類型

  1. RFC 解析器 — 此解析器類型期望 URI 字串符合 RFC 3986 語法,並將偏離語法的行為視為非法。

  2. WhatWG 解析器 — 此解析器基於 URL 解析演算法,位於 WhatWG URL Living 標準 中。它為各種意外輸入情況提供寬鬆的處理。瀏覽器實作此功能是為了寬鬆地處理使用者輸入的 URL。如需更多詳細資訊,請參閱 URL Living Standard 和 URL 解析 測試案例

預設情況下,RestClientWebClientRestTemplate 使用 RFC 解析器類型,並期望應用程式提供符合 RFC 語法的 URL 範本。若要變更此設定,您可以自訂任何客户端上的 UriBuilderFactory

應用程式和框架可能會進一步依賴 UriComponentsBuilder 來滿足自身需求,以解析使用者提供的 URL,以便檢查並可能驗證 URI 組件,例如 scheme、host、port、path 和 query。這些組件可以決定使用 WhatWG 解析器類型,以便更寬鬆地處理 URL,並與瀏覽器解析 URI 的方式保持一致,以防重新導向到輸入 URL 或輸入 URL 包含在對瀏覽器的回應中。

URI 編碼

Spring MVC 和 Spring WebFlux

UriComponentsBuilder 在兩個層級公開編碼選項

這兩個選項都會將非 ASCII 和非法字元取代為逸出八位元組。但是,第一個選項也會取代 URI 變數中出現的具有保留意義的字元。

考量 ";",它在路徑中是合法的,但具有保留意義。第一個選項會將 URI 變數中的 ";" 取代為 "%3B",但不會在 URI 範本中取代。相比之下,第二個選項永遠不會取代 ";",因為它是路徑中的合法字元。

對於大多數情況,第一個選項可能給出預期的結果,因為它將 URI 變數視為要完全編碼的不透明資料,而第二個選項在 URI 變數確實有意包含保留字元時很有用。當完全不展開 URI 變數時,第二個選項也很有用,因為這也會編碼任何看起來像 URI 變數的內容。

以下範例使用第一個選項

  • Java

  • Kotlin

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

您可以透過直接前往 URI(這意味著編碼)來縮短前面的範例,如下列範例所示

  • Java

  • Kotlin

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

您可以使用完整的 URI 範本進一步縮短它,如下列範例所示

  • Java

  • Kotlin

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

WebClientRestTemplate 在內部透過 UriBuilderFactory 策略展開和編碼 URI 範本。兩者都可以使用自訂策略進行組態,如下列範例所示

  • Java

  • Kotlin

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

DefaultUriBuilderFactory 實作在內部使用 UriComponentsBuilder 來展開和編碼 URI 範本。作為工廠,它提供了一個單一位置來組態編碼方法,基於以下編碼模式之一

  • TEMPLATE_AND_VALUES: 使用 UriComponentsBuilder#encode(),對應於先前列表中的第一個選項,以預先編碼 URI 範本,並在展開時嚴格編碼 URI 變數。

  • VALUES_ONLY: 不編碼 URI 範本,而是透過 UriUtils#encodeUriVariables 在將 URI 變數展開到範本之前對其應用嚴格編碼。

  • URI_COMPONENT: 使用 UriComponents#encode(),對應於先前列表中的第二個選項,在 URI 變數展開編碼 URI 組件值。

  • NONE: 不套用任何編碼。

由於歷史原因和向後相容性,RestTemplate 設定為 EncodingMode.URI_COMPONENTWebClient 依賴 DefaultUriBuilderFactory 中的預設值,該值已從 5.0.x 中的 EncodingMode.URI_COMPONENT 變更為 5.1 中的 EncodingMode.TEMPLATE_AND_VALUES