Exploring the Spring 5 WebFlux URL Matching – 探索Spring 5 WebFlux的URL匹配

最后修改: 2017年 6月 21日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Spring 5 brought a new PathPatternParser for parsing URI template patterns. This is an alternative to the previously used AntPathMatcher.

Spring 5 带来了新的PathPatternParser,用于解析URI模板模式这是对之前使用的AntPathMatcher的一种替代。

The AntPathMatcher was an implementation of Ant-style path pattern matching. PathPatternParser breaks the path into a linked list of PathElements. This chain of PathElements is taken by the PathPattern class for quick matching of patterns.

AntPathMatcher是Ant式路径模式匹配的实现。PathPatternParser将路径分解成一个PathElements的链接列表。这个PathElements链被PathPattern类拿去快速匹配模式。

With the PathPatternParser, support for a new URI variable syntax was also introduced.

通过PathPatternParser,还引入了对新URI变量语法的支持。

In this article, we’ll go through the new/updated URL pattern matchers introduced in Spring 5.0 WebFlux and also the ones that have been there since older versions of Spring.

在这篇文章中,我们将介绍Spring 5.0 WebFlux中引入的新的/更新的URL模式匹配器,以及从Spring旧版本开始就有的匹配器。

2. New URL Pattern Matchers in Spring 5.0

2.Spring 5.0中新的URL模式匹配器

The Spring 5.0 release added a very easy to use URI variable syntax: {*foo} to capture any number of path segments at the end of the pattern.

Spring 5.0版本增加了一个非常容易使用的URI变量语法。{*foo}来捕捉模式末端的任何数量的路径段。

2.1. URI Variable Syntax {*foo} Using a Handler Method

2.1.URI变量的语法{*foo} 使用处理程序方法

Let’s see an example of the URI variable pattern {*foo} another example using @GetMapping and a handler method. Whatever we give in the path after “/spring5” will be stored in the path variable “id”:

让我们看一个URI变量模式的例子 {*foo}另一个使用@GetMapping和一个处理方法的例子。无论我们在“/spring5”之后的路径中给出什么,都将被存储在路径变量 “id “中。

@GetMapping("/spring5/{*id}")
public String URIVariableHandler(@PathVariable String id) {
    return id;
}
@Test
public void givenHandlerMethod_whenMultipleURIVariablePattern_then200() {
        
    client.get()
      .uri("/spring5/baeldung/tutorial")
      .exchange()
      .expectStatus()
      .is2xxSuccessful()
      .expectBody()
      .equals("/baeldung/tutorial");

    client.get()
      .uri("/spring5/baeldung")
      .exchange()
      .expectStatus()
      .is2xxSuccessful()
      .expectBody()
      .equals("/baeldung");
}

2.2. URI Variable Syntax {*foo} Using RouterFunction

2.2.URI变量语法{*foo} 使用RouterFunction

Let’s see an example of the new URI variable path pattern using RouterFunction:

让我们看一个使用RouterFunction的新URI变量路径模式的例子。

private RouterFunction<ServerResponse> routingFunction() {
    return route(GET("/test/{*id}"), 
      serverRequest -> ok().body(fromValue(serverRequest.pathVariable("id"))));
}

In this case, whatever path we write after “/test” will be captured in the path variable “id”. So the test case for it could be:

在这种情况下,无论我们在”/test “后面写什么路径,都会在路径变量 “id “中捕获。所以它的测试案例可以是。

@Test
public void givenRouter_whenMultipleURIVariablePattern_thenGotPathVariable() 
  throws Exception {
 
    client.get()
      .uri("/test/ab/cd")
      .exchange()
      .expectStatus()
      .isOk()
      .expectBody(String.class)
      .isEqualTo("/ab/cd");
}

2.3. Use of URI Variable Syntax {*foo} to Access Resources

2.3.使用URI变量语法{*foo}来访问资源

If we want to access resources, then we’ll need to write the similar path pattern as we wrote in the previous example.

如果我们想访问资源,那么我们就需要写出与前面例子中类似的路径模式。

So let’s say our pattern is: “/files/{*filepaths}”. In this case, if the path is /files/hello.txt, the value of path variable “filepaths” will be “/hello.txt”, whereas, if the path is /files/test/test.txt, the value of “filepaths” = “/test/test.txt”.

因此,让我们说我们的模式是。“/files/{*filepaths}”。在这种情况下,如果路径是/files/hello.txt,路径变量“filepaths”的值将是”/hello.txt”,而如果路径是/files/test/test.txt,“filepaths”的值 = “/test/test.txt”。

Our routing function for accessing file resources under the /files/ directory:

我们的路由功能用于访问/files/目录下的文件资源。

private RouterFunction<ServerResponse> routingFunction() { 
    return RouterFunctions.resources(
      "/files/{*filepaths}", 
      new ClassPathResource("files/"))); 
}

Let’s assume that our text files hello.txt and test.txt contain “hello” and “test” respectively. This can be demonstrated with a JUnit test case:

让我们假设我们的文本文件 hello.txttest.txt分别包含“hello”/em>和“test”/em>。这可以用一个JUnit测试案例来证明。

@Test 
public void givenResources_whenAccess_thenGot() 
  throws Exception { 
      client.get() 
        .uri("/files/test/test.txt") 
        .exchange() 
        .expectStatus() 
        .isOk() 
        .expectBody(String.class) 
        .isEqualTo("test");
 
      client.get() 
        .uri("/files/hello.txt") 
        .exchange() 
        .expectStatus() 
        .isOk() 
        .expectBody(String.class) 
        .isEqualTo("hello"); 
}

3. Existing URL Patterns from Previous Versions

3.以前版本的现有URL模式

Let’s now take a peek into all the other URL pattern matchers that have been supported by older versions of Spring. All these patterns work with both RouterFunction and Handler methods with @GetMapping.

现在让我们来看看Spring旧版本所支持的所有其他URL模式匹配器。所有这些模式都与RouterFunction和Handler方法一起使用@GetMapping

3.1. ‘?’ Matches Exactly One Character

3.1.’? ‘正好匹配一个字符

If we specify the path pattern as: “/t?st“, this will match paths like: “/test” and “/tast”, but not “/tst” and “/teest”.

如果我们指定路径模式为。“/t?st“,这将匹配类似的路径。“/test”“/tast”,但不包括“/tst”“/teest”。

The example code using RouterFunction and its JUnit test case:

使用RouterFunction的示例代码及其JUnit测试案例。

private RouterFunction<ServerResponse> routingFunction() { 
    return route(GET("/t?st"), 
      serverRequest -> ok().body(fromValue("Path /t?st is accessed"))); 
}

@Test
public void givenRouter_whenGetPathWithSingleCharWildcard_thenGotPathPattern()   
  throws Exception {
 
      client.get()
        .uri("/test")
        .exchange()
        .expectStatus()
        .isOk()
        .expectBody(String.class)
        .isEqualTo("Path /t?st is accessed");
}

3.2. ‘*’ Matches 0 or More Characters Within a Path Segment

3.2.’*’在一个路径段中匹配0个或更多的字符

If we specify the path pattern as : “/baeldung/*Id”, this will match path patterns like:”/baeldung/Id”, “/baeldung/tutorialId”, “/baeldung/articleId”, etc:

如果我们指定路径模式为:“/baeldung/*Id”,这将匹配路径模式如:”/baeldung/Id”,”/baeldung/tutorialId”,“/baeldung/articleId”,等等。

private RouterFunction<ServerResponse> routingFunction() { 
    returnroute(
      GET("/baeldung/*Id"), 
      serverRequest -> ok().body(fromValue("/baeldung/*Id path was accessed"))); }

@Test
public void givenRouter_whenGetMultipleCharWildcard_thenGotPathPattern() 
  throws Exception {
      client.get()
        .uri("/baeldung/tutorialId")
        .exchange()
        .expectStatus()
        .isOk()
        .expectBody(String.class)
        .isEqualTo("/baeldung/*Id path was accessed");
}

3.3. ‘**’ Matches 0 or More Path Segments Until the End of the Path

3.3.’**’匹配了0个或更多的路径段,直到路径的末端

In this case, the pattern matching is not limited to a single path segment. If we specify the pattern as “/resources/**”, it will match all paths to any number of path segments after “/resources/”:

在这种情况下,模式匹配并不局限于单个路径段。如果我们指定模式为“/resources/**”,它将匹配“/resources/”之后的任何数量的路径段的所有路径:

private RouterFunction<ServerResponse> routingFunction() { 
    return RouterFunctions.resources(
      "/resources/**", 
      new ClassPathResource("resources/"))); 
}

@Test
public void givenRouter_whenAccess_thenGot() throws Exception {
    client.get()
      .uri("/resources/test/test.txt")
      .exchange()
      .expectStatus()
      .isOk()
      .expectBody(String.class)
      .isEqualTo("content of file test.txt");
}

3.4. ‘{baeldung:[a-z]+}’ Regex in Path Variable

3.4. ‘{baeldung:[a-z]+}’路径变量中的 Regex

We can also specify a regex for the value of path variable. So if our pattern is like “/{baeldung:[a-z]+}”, the value of path variable “baeldung” will be any path segment that matches the gives regex:

我们还可以为路径变量的值指定一个重合词。因此,如果我们的模式是“/{baeldung:[a-z]+}”,路径变量“baeldung”的值将是任何与给定的重合词匹配的路径段。

private RouterFunction<ServerResponse> routingFunction() { 
    return route(GET("/{baeldung:[a-z]+}"), 
      serverRequest ->  ok()
        .body(fromValue("/{baeldung:[a-z]+} was accessed and "
        + "baeldung=" + serverRequest.pathVariable("baeldung")))); 
}

@Test
public void givenRouter_whenGetRegexInPathVarible_thenGotPathVariable() 
  throws Exception {
 
      client.get()
        .uri("/abcd")
        .exchange()
        .expectStatus()
        .isOk()
        .expectBody(String.class)
        .isEqualTo("/{baeldung:[a-z]+} was accessed and "
          + "baeldung=abcd");
}

3.5. ‘/{var1}_{var2}’ Multiple Path Variables in Same Path Segment

3.5.‘/{var1}_{var2}’同一路径段的多个路径变量

Spring 5 made sure that multiple path variables will be allowed in a single path segment only when separated by a delimiter. Only then can Spring distinguish between the two different path variables:

Spring 5确保只有在用分隔符分隔的情况下,才允许在一个路径段中出现多个路径变量。只有这样,Spring才能区分这两个不同的路径变量。

private RouterFunction<ServerResponse> routingFunction() { 
 
    return route(
      GET("/{var1}_{var2}"),
      serverRequest -> ok()
        .body(fromValue( serverRequest.pathVariable("var1") + " , " 
        + serverRequest.pathVariable("var2"))));
 }

@Test
public void givenRouter_whenGetMultiplePathVaribleInSameSegment_thenGotPathVariables() 
  throws Exception {
      client.get()
        .uri("/baeldung_tutorial")
        .exchange()
        .expectStatus()
        .isOk()
        .expectBody(String.class)
        .isEqualTo("baeldung , tutorial");
}

4. Conclusion

4.总结

In this article, we went through the new URL matchers in Spring 5, as well as the ones that are available in older versions of Spring.

在这篇文章中,我们浏览了Spring 5中新的URL匹配器,以及Spring旧版本中可用的匹配器。

As always, the implementation of all the examples we discussed can be found over on GitHub.

一如既往,我们所讨论的所有例子的实现都可以在GitHub上找到over