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
建立 UriBuilder
。UriBuilderFactory
和 UriBuilder
一起提供一個可插拔的機制,用於從 URI 範本建立 URI,基於共享組態,例如基本 URL、編碼偏好和其他詳細資訊。
您可以組態 RestTemplate
和 WebClient
與 UriBuilderFactory
,以自訂 URI 的準備方式。DefaultUriBuilderFactory
是 UriBuilderFactory
的預設實作,它在內部使用 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 解析器類型
-
RFC 解析器 — 此解析器類型期望 URI 字串符合 RFC 3986 語法,並將偏離語法的行為視為非法。
-
WhatWG 解析器 — 此解析器基於 URL 解析演算法,位於 WhatWG URL Living 標準 中。它為各種意外輸入情況提供寬鬆的處理。瀏覽器實作此功能是為了寬鬆地處理使用者輸入的 URL。如需更多詳細資訊,請參閱 URL Living Standard 和 URL 解析 測試案例。
預設情況下,RestClient
、WebClient
和 RestTemplate
使用 RFC 解析器類型,並期望應用程式提供符合 RFC 語法的 URL 範本。若要變更此設定,您可以自訂任何客户端上的 UriBuilderFactory
。
應用程式和框架可能會進一步依賴 UriComponentsBuilder
來滿足自身需求,以解析使用者提供的 URL,以便檢查並可能驗證 URI 組件,例如 scheme、host、port、path 和 query。這些組件可以決定使用 WhatWG 解析器類型,以便更寬鬆地處理 URL,並與瀏覽器解析 URI 的方式保持一致,以防重新導向到輸入 URL 或輸入 URL 包含在對瀏覽器的回應中。
URI 編碼
Spring MVC 和 Spring WebFlux
UriComponentsBuilder
在兩個層級公開編碼選項
-
UriComponentsBuilder#encode(): 首先預先編碼 URI 範本,然後在展開時嚴格編碼 URI 變數。
-
UriComponents#encode(): 在 URI 變數展開後編碼 URI 組件。
這兩個選項都會將非 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")
WebClient
和 RestTemplate
在內部透過 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_COMPONENT
。WebClient
依賴 DefaultUriBuilderFactory
中的預設值,該值已從 5.0.x 中的 EncodingMode.URI_COMPONENT
變更為 5.1 中的 EncodingMode.TEMPLATE_AND_VALUES
。