探索Spring 5 WebFlux URL匹配

1.概述

Spring 5帶來了一個新的PathPatternParser用於解析URI模板模式.這是以前使用的AntPathMatcher的替代方法。

AntPathMatcher是Ant樣式路徑模式匹配的實現。 PathPatternParser將路徑分為PathElements的鏈接列表。這一鏈條PathElements採取的是PathPattern的模式快速匹配的類。

通過PathPatternParser ,還引入了對新URI變量語法的支持。

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

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

Spring 5.0發行版添加了一個非常易於使用的URI變量語法:{* foo},以在模式末尾捕獲任意數量的路徑段。

2.1。使用處理程序方法的URI變量語法{* foo}

我們來看一個URI變量模式{*foo}示例,另一個使用@GetMapping和處理程序方法的示例。我們在“/spring5”之後的路徑中“/spring5”都將存儲在路徑變量“ id”中:

@GetMapping("/spring5/{*id}")

 public String URIVariableHandler(@PathVariable String id) {

 return id;

 }
@Test

 public void whenMultipleURIVariablePattern_thenGotPathVariable() {



 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。使用RouterFunction URI變量語法{* foo}

讓我們來看一個使用RouterFunction的新URI變量路徑模式的RouterFunction

private RouterFunction<ServerResponse> routingFunction() {

 return route(GET("/test/{*id}"),

 serverRequest -> ok().body(fromObject(serverRequest.pathVariable("id"))));

 }

在這種情況下,我們在“ / test”之後編寫的任何路徑都將被捕獲在路徑變量“ id”中。因此,測試用例可能是:

@Test

 public void 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}”.在這種情況下,如果路徑為/files/hello.txt,則路徑變量“filepaths”的值為“ /hello.txt”,而如果路徑為/files/test/test.txt,則路徑值為“filepaths” =“ /test/test.txt”。

我們的路由功能用於訪問/files/目錄下的文件資源:

private RouterFunction<ServerResponse> routingFunction() {

 return RouterFunctions.resources(

 "/files/{*filepaths}",

 new ClassPathResource("files/")));

 }

假設我們的文本文件hello.txttest.txt包含“hello”“test” 。這可以通過JUnit測試用例進行演示:

@Test

 public void whenMultipleURIVariablePattern_thenGotPathVariable()

 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模式匹配器。所有這些模式都可以通過RouterFunctionRouterFunction和Handler方法一起@GetMapping

3.1。 '?'完全匹配一個字符

如果我們將路徑模式指定為: “/t? st “,則將匹配以下路徑: “/test”“/tast”,但不匹配“/tst”“/teest”.

使用RouterFunction及其JUnit測試用例的示例代碼:

private RouterFunction<ServerResponse> routingFunction() {

 return route(GET("/t?st"),

 serverRequest -> ok().body(fromObject("Path /t?st is accessed")));

 }



 @Test

 public void whenGetPathWithSingleCharWildcard_thenGotPathPattern()

 throws Exception {



 client.get()

 .uri("/test")

 .exchange()

 .expectStatus()

 .isOk()

 .expectBody(String.class)

 .isEqualTo("Path /t?st is accessed");

 }

3.2。 '*'匹配路徑段中的0個或更多字符

如果我們將路徑模式指定為: “/baeldung/*Id”,則將匹配以下路徑模式like:”/baeldung/Id”, “/baeldung/tutorialId”, “ / baeldung / articleId”等:

private RouterFunction<ServerResponse> routingFunction() {

 returnroute(

 GET("/baeldung/*Id"),

 serverRequest -> ok().body(fromObject("/baeldung/*Id path was accessed"))); }



 @Test

 public void 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/”:之後的任意數量的路徑段匹配“/resources/”:

private RouterFunction<ServerResponse> routingFunction() {

 return RouterFunctions.resources(

 "/resources/**",

 new ClassPathResource("resources/")));

 }



 @Test

 public void 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:[az]+}'表達式

我們還可以為path變量的值指定一個正則表達式。因此,如果我們的模式類似於“/{baeldung:[az]+}”,則路徑變量“baeldung”的值將是與給定正則表達式匹配的任何路徑段:

private RouterFunction<ServerResponse> routingFunction() {

 return route(GET("/{baeldung:[az]+}"),

 serverRequest -> ok()

 .body(fromObject("/{baeldung:[az]+} was accessed and "

 + "baeldung=" + serverRequest.pathVariable("baeldung"))));

 }



 @Test

 public void whenGetRegexInPathVarible_thenGotPathVariable()

 throws Exception {



 client.get()

 .uri("/abcd")

 .exchange()

 .expectStatus()

 .isOk()

 .expectBody(String.class)

 .isEqualTo("/{baeldung:[az]+} was accessed and "

 + "baeldung=abcd");

 }

3.5。 '/{var1}_{var2}'同一路徑段中'/{var1}_{var2}'多個路徑變量

Spring 5確保只有在由定界符分隔時,才允許在單個路徑段中使用多個路徑變量。只有這樣,Spring才能區分兩個不同的路徑變量:

private RouterFunction<ServerResponse> routingFunction() {



 return route(

 GET("/{var1}_{var2}"),

 serverRequest -> ok()

 .body(fromObject( serverRequest.pathVariable("var1") + " , "

 + serverRequest.pathVariable("var2"))));

 }



 @Test

 public void whenGetMultiplePathVaribleInSameSegment_thenGotPathVariables()

 throws Exception {

 client.get()

 .uri("/baeldung_tutorial")

 .exchange()

 .expectStatus()

 .isOk()

 .expectBody(String.class)

 .isEqualTo("baeldung , tutorial");

 }

4。結論

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

與往常一樣,我們討論的所有示例的實現都可以在GitHub上找到