Перейти к основному содержимому

Изучение сопоставления URL-адресов Spring 5 WebFlux

· 5 мин. чтения

1. Обзор

В Spring 5 появился новый PathPatternParser для разбора шаблонов шаблонов URI . Это альтернатива ранее использовавшемуся AntPathMatcher .

AntPathMatcher был реализацией сопоставления пути с образцом в стиле Ant. PathPatternParser разбивает путь на связанный список PathElements . Эта цепочка PathElements используется классом PathPattern для быстрого сопоставления шаблонов.

С PathPatternParser также была введена поддержка нового синтаксиса переменной URI.

В этой статье мы рассмотрим новые/обновленные средства сопоставления шаблонов URL, представленные в Spring 5.0 WebFlux, а также те, которые появились в более старых версиях Spring.

2. Новые средства сопоставления шаблонов URL в Spring 5.0

В выпуске Spring 5.0 добавлен очень простой в использовании синтаксис переменной URI: {*foo} для захвата любого количества сегментов пути в конце шаблона.

2.1. Синтаксис переменной URI {*foo} с использованием метода обработчика

Давайте посмотрим на пример шаблона переменной URI {*foo} еще один пример с использованием @GetMapping и метода обработчика. Все, что мы укажем в пути после «/spring5» , будет сохранено в переменной пути «id»:

@GetMapping("/spring5/{*id}")
public String URIVariableHandler(@PathVariable String id) {
return id;
}
@Test
public void whenMultipleURIVariablePattern_thenGotPathVariable() {

client.get()
.uri("/spring5/foreach/tutorial")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.equals("/foreach/tutorial");

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

2.2. Синтаксис переменной URI {*foo} с использованием RouterFunction

Давайте посмотрим на пример нового шаблона пути переменной URI с использованием RouterFunction :

private RouterFunction<ServerResponse> routingFunction() {
return route(GET("/test/{*id}"),
serverRequest -> ok().body(fromValue(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, значение « пути к файлам» = «/test/test.txt».

Наша функция маршрутизации для доступа к файловым ресурсам в каталоге /files/ :

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

Предположим, что наши текстовые файлы hello.txt и test.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 из предыдущих версий

Давайте теперь взглянем на все другие сопоставители шаблонов URL, которые поддерживались более старыми версиями Spring. Все эти шаблоны работают как с методами RouterFunction , так и с методами Handler с @GetMapping .

3.1. '?' Соответствует ровно одному символу

Если мы укажем шаблон пути как: «/t? st », это будет соответствовать путям типа: «/test» и «/tast», но не «/tst» и «/teest».

Пример кода с использованием RouterFunction и его тестового примера JUnit:

private RouterFunction<ServerResponse> routingFunction() { 
return route(GET("/t?st"),
serverRequest -> ok().body(fromValue("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 или более символам в сегменте пути

Если мы укажем шаблон пути как: «/foreach/*Id», это будет соответствовать шаблонам пути, таким как: «/foreach/Id», «/foreach/tutorialId», «/foreach/articleId» и т. д.:

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

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

3.3. '' Соответствует 0 или более сегментам пути до конца пути**

В этом случае сопоставление с шаблоном не ограничивается одним сегментом пути. Если мы укажем шаблон как «/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. '{foreach:[az]+}' Регулярное выражение в переменной пути

Мы также можем указать регулярное выражение для значения переменной пути. Итак, если наш шаблон похож на «/{foreach:[az]+}», значение переменной пути «foreach» будет любым сегментом пути, который соответствует регулярному выражению Gives:

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

@Test
public void whenGetRegexInPathVarible_thenGotPathVariable()
throws Exception {

client.get()
.uri("/abcd")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("/{foreach:[a-z]+} was accessed and "
+ "foreach=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 whenGetMultiplePathVaribleInSameSegment_thenGotPathVariables()
throws Exception {
client.get()
.uri("/foreach_tutorial")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("foreach , tutorial");
}

4. Вывод

В этой статье мы рассмотрели новые средства сопоставления URL в Spring 5, а также те, которые доступны в более старых версиях Spring.

Как всегда, реализацию всех рассмотренных нами примеров можно найти на GitHub .