介绍

在本教程中,我们将重点介绍 Spring UriComponentsBuilder。更具体地说,我们将介绍各种实际的实现示例。

UriComponentsBuilder 是 Spring 提供的一个 UriComponents 类的构建类,通过它可以方便的构建 URI 地址。

Maven 依赖

为了使用构建器,我们需要在 pom.xml 的依赖项中添加以下部分:

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-web</artifactId>
  4. <version>5.2.2.RELEASE</version>
  5. </dependency>

最新版本可以在这里找到:https://search.maven.org/search?q=a:spring-web%20AND%20g:org.springframework

这种依赖关系仅涵盖 Spring Web,因此请不要忘记为完整的 Web 应用程序添加 spring-context。

实例化方法

  1. public static UriComponentsBuilder newInstance();
  2. public static UriComponentsBuilder fromPath(String path);
  3. public static UriComponentsBuilder fromUri(URI uri);
  4. public static UriComponentsBuilder fromUriString(String uri);
  5. public static UriComponentsBuilder fromHttpUrl(String httpUrl);
  6. public static UriComponentsBuilder fromHttpRequest(HttpRequest request);
  7. public static UriComponentsBuilder fromOriginHeader(String origin);

UriComponentsBuilder 提供了 7 种实例化的方法,根据不同的需求选择对应的方法实例化 UriComponentsBuilder 对象。

对于 fromHttpRequest() 和 fromOriginHeader() 方法的使用参考如下案例:

  1. public static boolean isSameOrigin(HttpRequest request) {
  2. String origin = request.getHeaders().getOrigin();
  3. if (origin == null) {
  4. return true;
  5. }
  6. UriComponents actualUrl = UriComponentsBuilder.fromHttpRequest(request).build();
  7. UriComponents originUrl = UriComponentsBuilder.fromOriginHeader(origin).build();
  8. return (actualUrl.getHost().equals(originUrl.getHost()) && getPort(actualUrl) == getPort(originUrl));
  9. }

使用案例

UriComponentsBuilder 有许多实际的用例,从对应的 URI 组件中不允许的字符的上下文编码开始,到动态替换 URL 的各个部分为止。

UriComponentsBuilder 的最大优点之一是我们可以将其直接注入到 Controller 方法中:

  1. @RequestMapping(method = RequestMethod.POST)
  2. public ResponseEntity createCustomer(UriComponentsBuilder builder) {
  3. // implementation
  4. }

让我们开始一个一个地描述示例,我们将使用 JUnit 框架立即测试我们的实现。

构建一个简单的 URI

让我们从最简单的开始,使用 UriComponentsBuilder 创建一个简单的链接:

  1. @Test
  2. public void constructUri() {
  3. UriComponents uriComponents = UriComponentsBuilder.newInstance()
  4. .scheme("http").host("www.baeldung.com").path("/junit-5").build();
  5. assertEquals("/junit-5", uriComponents.toUriString());
  6. }

如我们所见,我们创建了一个新的 UriComponentsBuilder 实例,然后提供了 scheme 类型,host 和到请求目标的路径。

构建一个编码的 URI

除了构建简单的链接外,我们可能还希望对最终结果进行编码。让我们看看下面这个例子:

  1. @Test
  2. public void constructUriEncoded() {
  3. UriComponents uriComponents = UriComponentsBuilder.newInstance()
  4. .scheme("http").host("www.baeldung.com").path("/junit 5").build().encode();
  5. assertEquals("/junit%205", uriComponents.toUriString());
  6. }

这个例子的不同之处在于,我们要在 junit 和数字 5 之间添加空格。根据 RFC 3986,这是不可能的。我们需要使用 encode() 方法对链接进行编码,以达到有效的结果。

从模板构建一个 URI

URI 模板允许在 URI 的大多数组件中使用,但其值仅限于特定的元素,我们将其作为模板。让我们看看下面这个例子:

  1. @Test
  2. public void constructUriFromTemplate() {
  3. UriComponents uriComponents = UriComponentsBuilder.newInstance()
  4. .scheme("http").host("www.baeldung.com").path("/{article-name}")
  5. .buildAndExpand("junit-5");
  6. assertEquals("/junit-5", uriComponents.toUriString());
  7. }

在这个例子中,不同之处在于我们声明路径的方式和构建最终 URI 的方式。在 path() 方法里面,将被关键字替换的模板用括号 {…} 表示。在名为 buildAndExpand() 的方法中使用了用于生成最终链接的关键字。

请注意,可能有多个关键字要被替换。另外,URI 的路径可以是相对的。

使用查询参数构建 URI

另一个非常有用的情况是构建带有查询参数的 URI。

我们需要使用 UriComponentsBuilder 的 query() 方法来指定 URI 的查询参数。让我们看看下面的例子:

  1. @Test
  2. public void constructUriWithQueryParameter() {
  3. UriComponents uriComponents = UriComponentsBuilder.newInstance()
  4. .scheme("http").host("www.google.com")
  5. .path("/").query("q={keyword}").buildAndExpand("baeldung");
  6. assertEquals("http://www.google.com/?q=baeldung", uriComponents.toUriString());
  7. }

该查询将被添加到链接的主要部分。我们可以提供多个查询参数,使用括号 {…}。它们将在名为 buildAndExpand() 的方法中被关键字替换。

使用正则表达式扩展 URI

最后一个例子展示的是一个带有 regex 验证的 URI 的构造。只有在 regex 验证成功的情况下,我们才能扩展 uriComponents。

  1. @Test
  2. public void expandWithRegexVar() {
  3. String template = "/myurl/{name:[a-z]{1,5}}/show";
  4. UriComponents uriComponents = UriComponentsBuilder.fromUriString(template)
  5. .build();
  6. uriComponents = uriComponents.expand(Collections.singletonMap("name", "test"));
  7. assertEquals("/myurl/test/show", uriComponents.getPath());
  8. }

在上述例子中,我们可以看到,链接的中间部分只能添加 a-z 的字母,长度在1-5之间。

另外,我们还使用了 singletonMap,将关键字名替换为值。

当我们允许用户动态地指定链接,但我们想提供一种安全保障,只有有效的链接才能在我们的 Web 应用程序中工作时,这个例子特别有用。

转载

作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/uup1gh 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。