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.txt 和 test.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 中可用的匹配器。