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 .