«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/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
private RouterFunction<ServerResponse> routingFunction() {
return route(GET("/test/{*id}"),
serverRequest -> ok().body(fromValue(serverRequest.pathVariable("id"))));
}
Давайте посмотрим на пример нового шаблона пути переменной URI с использованием RouterFunction:
@Test
public void whenMultipleURIVariablePattern_thenGotPathVariable()
throws Exception {
client.get()
.uri("/test/ab/cd")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("/ab/cd");
}
В этом случае любой путь, который мы напишем после «/test», будет захвачен. в переменной пути «id». Таким образом, тестовый пример для этого может быть:
2.3. Использование синтаксиса переменной URI {*foo} для доступа к ресурсам
Если мы хотим получить доступ к ресурсам, нам нужно написать аналогичный шаблон пути, как мы написали в предыдущем примере.
Допустим, наш шаблон таков: «/files/{*filepaths}». В этом случае, если путь — /files/hello.txt, значение переменной пути «filepaths» будет «/hello.txt», тогда как, если путь — /files/test/test.txt, значение «filepaths» = «/test/test.txt».
private RouterFunction<ServerResponse> routingFunction() {
return RouterFunctions.resources(
"/files/{*filepaths}",
new ClassPathResource("files/")));
}
Наша функция маршрутизации для доступа к файловым ресурсам в каталоге /files/:
@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");
}
Предположим, что наши текстовые файлы hello.txt и test.txt содержат «hello» и «test» соответственно. Это можно продемонстрировать с помощью тестового примера JUnit:
3. Существующие шаблоны URL из предыдущих версий
Давайте теперь взглянем на все другие средства сопоставления шаблонов URL, которые поддерживались более ранними версиями Spring. Все эти шаблоны работают как с методами RouterFunction, так и с методами Handler с @GetMapping.
3.1. -? Соответствует ровно одному символу
Если мы укажем шаблон пути как: «/t?st», это будет соответствовать таким путям, как: «/test» и «/tast», но не « /tst” и “/teest”.
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");
}
Пример кода, использующего RouterFunction и его тестовый сценарий JUnit:
3.2. «*» соответствует 0 или более символам в сегменте пути
private RouterFunction<ServerResponse> routingFunction() {
returnroute(
GET("/baeldung/*Id"),
serverRequest -> ok().body(fromValue("/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");
}
Если мы укажем шаблон пути как: «/baeldung/*Id», это будет соответствовать шаблонам пути, например: «/baeldung/Id», «/baeldung/tutorialId», «/baeldung/articleId» и т. д.:
3.3. ‘**’ Соответствует 0 или более сегментам пути до конца пути
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");
}
В этом случае сопоставление с шаблоном не ограничивается одним сегментом пути. Если мы укажем шаблон как «/resources/**», он будет соответствовать всем путям до любого количества сегментов пути после «/resources/»:
3.4. ‘{baeldung:[a-z]+}’ Регулярное выражение в переменной пути
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 whenGetRegexInPathVarible_thenGotPathVariable()
throws Exception {
client.get()
.uri("/abcd")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("/{baeldung:[a-z]+} was accessed and "
+ "baeldung=abcd");
}
Мы также можем указать регулярное выражение для значения переменной пути. Итак, если наш шаблон похож на «/{baeldung:[a-z]+}», значение переменной пути «baeldung» будет любым сегментом пути, который соответствует регулярному выражению Gives:
3.5. ‘/{var1}_{var2}’ Несколько переменных пути в одном сегменте пути
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("/baeldung_tutorial")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("baeldung , tutorial");
}
Spring 5 гарантирует, что несколько переменных пути будут разрешены в одном сегменте пути, только если они разделены разделителем. Только тогда Spring сможет различать две разные переменные пути:
4. Заключение
В этой статье мы рассмотрели новые сопоставители URL в Spring 5, а также те, которые доступны в более старых версиях. весны.