UriComponentsBuilder 在两个层面暴露了编码选项。
- UriComponentsBuilder#encode():首先对 URI 模板进行预编码,然后在展开时对 URI 变量进行严格编码。
- UriComponents#encode():在 URI 变量扩展后对 URI 组件进行编码。
这两个选项都用转义的八位数替换非 ASCII 和非法字符。然而,第一个选项也取代了 URI 变量中出现的具有保留意义的字符。
:::info 考虑到”;”,它在路径中是合法的,但有保留意义。第一个选项在 URI 变量中用 “%3B “ 替换了 “;”,但在 URI 模板中没有。相比之下,第二个选项从不替换 “;”,因为它在路径中是一个合法字符。 :::
在大多数情况下,第一个选项可能会得到预期的结果,因为它将 URI 变量作为不透明的数据进行完全编码,而第二个选项在 URI 变量有意包含保留字符时很有用。第二个选项在完全不扩展 URI 变量时也很有用,因为这也会对任何偶然看起来像 URI 变量的东西进行编码。
下面的例子使用的是第一个选项:
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("New York", "foo+bar")
.toUri();
// 构建的结果是 "/hotel%20list/New%20York?q=foo%2Bbar"
你可以通过直接进入 URI(这意味着编码)来缩短前面的例子,如下例所示:
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.build("New York", "foo+bar");
你还可以用一个完整的 URI 模板进一步缩短它,如下例所示:
URI uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
.build("New York", "foo+bar");
WebClient 和 RestTemplate 通过 UriBuilderFactory 策略在内部扩展和编码 URI 模板。如下面的例子所示,两者都可以用自定义策略进行配置:
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();
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。