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
一起提供了一種可插拔的機制,用於根據共享配置(例如基本 URL、編碼偏好及其他詳細資訊)從 URI 模板構建 URI。
你可以使用 UriBuilderFactory
配置 RestTemplate
和 WebClient
,以定製 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 解析器——此解析器基於 WhatWG URL Living Standard 中的 URL 解析演算法。它提供對各種意外輸入的寬鬆處理。瀏覽器實現此功能是為了寬鬆地處理使用者輸入的 URL。有關更多詳細資訊,請參閱 URL Living Standard 和 URL 解析測試用例。
預設情況下,RestClient
、WebClient
和 RestTemplate
使用 RFC 解析器型別,並期望應用程式提供符合 RFC 語法的 URL 模板。要更改此設定,你可以在任何客戶端上定製 UriBuilderFactory
。
應用程式和框架可以進一步依賴 UriComponentsBuilder
來解析使用者提供的 URL,以便檢查並可能驗證 URI 元件,例如 scheme、host、port、path 和 query。這些元件可以決定使用 WhatWG 解析器型別來更寬鬆地處理 URL,並在重定向到輸入 URL 或響應中包含該 URL 的情況下,與瀏覽器解析 URI 的方式保持一致。
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
。