知識庫 / Spring WebFlux RSS 訂閱

Spring WebFlux URL 匹配探索

Spring WebFlux
HongKong
5
02:32 PM · Dec 06 ,2025

1. 概述

Spring 5 帶來了 PathPatternParser,用於解析 URI 模板模式。 它是之前使用的 AntPathMatcher 的替代方案。

AntPathMatcher 是 Ant 風格路徑模式匹配的實現。 PathPatternParser 將路徑分解為 PathElements 的鏈表。 這種 PathElements 鏈表由 PathPattern 類用於快速匹配模式。

隨着 PathPatternParser 的引入,還支持了新的 URI 變量語法。

在本文中,我們將介紹 Spring 5.0 WebFlux 中引入的、以及在較早版本的 Spring 中一直存在的新的/更新的 URL 模式匹配器

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

Spring 5.0 發佈版本新增了一種易於使用的 URI 變量語法:{*foo} 用於捕獲模式末尾的任意數量的路徑片段。

2.1. URI 變量語法 {*foo} 使用 Handler 方法

讓我們來看一個 URI 變量模式 {*foo} 的示例, 另一個示例使用 @GetMapping 和一個 Handler 方法。 我們在 "/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 變量語法 {*foo} 使用 RouterFunction

以下是一個使用 RouterFunction 的新 URI 變量路徑模式的示例:

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

無論我們寫在“/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. 使用 URI 變量語法 {*foo} 訪問資源

如果需要訪問資源,我們需要編寫與之前示例中類似的路徑模式。

例如,我們的模式是:“/files/{*filepaths}”</em/>。在這種情況下,如果路徑是 "/files/hello.txt"</em/>,則路徑變量 "filepaths"</em/> 的值為 "/hello.txt",而如果路徑是 "/files/test/test.txt"</em/>,則路徑變量 "filepaths"</em/> 的值為 "/test/test.txt"。

我們的路由函數用於在 "/files/"</em/> 目錄下訪問文件資源:

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

假設我們的文本文件hello.txttest.txt 包含 “hello”“test”, respectively。 這可以通過一個 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. 以前版本中已存在的 URL 模式

現在,讓我們來查看一下以前版本的 Spring 支持的所有其他 URL 模式匹配器。這些模式與 RouterFunction 和帶有 @GetMapping 註解的 Handler 方法都兼容。

3.1. ‘?’ 匹配單個字符

如果將路徑模式指定為:“/t?</em style="font-style: italic;">st</em style="font-style: italic;">,則會匹配諸如:“/test”</em style="font-style: italic;">和“/tast”</em style="font-style: italic;">,但不匹配“/tst”</em style="font-style: italic;">和“/teest”</em style="font-style: italic;">。

以下使用 RouterFunction</em style="font-style: italic;">及其 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. ‘*’ 匹配路徑片段中的 0 個或多個字符

如果我們將路徑模式指定為:<em “/baeldung/*Id”, 則它將匹配路徑模式,例如:<em “/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. ‘**’ 匹配 0 個或多個路徑片段直到路徑結束

在這種情況下,模式匹配不限於單個路徑片段。如果我們指定模式為 “/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. 使用正則表達式指定路徑變量的值

我們可以為路徑變量的值指定一個正則表達式。例如,如果我們的模式是 <em “/{baeldung:[a-z]+}”,則路徑變量 <em “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})

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. 結論

在本文中,我們回顧了 Spring 5 中新 URL 匹配器,以及舊版本 Spring 中可用的匹配器。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.