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

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

1. обзор

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

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

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

В этой статьеwe’ll go through the new/updated URL pattern matchers introduced in 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/example/tutorial")
      .exchange()
      .expectStatus()
      .is2xxSuccessful()
      .expectBody()
      .equals("/example/tutorial");

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

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

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

private RouterFunction 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 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 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 или более символов в сегменте пути *

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

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

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

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

В этом случае сопоставление с образцом не ограничивается одним сегментом пути. Если мы укажем шаблон как“/resources/**”,, он сопоставит все пути с любым количеством сегментов пути после“/resources/”:

private RouterFunction 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. ‘\{example:[a-z]+}' Regex в переменной пути

Мы также можем указать регулярное выражение для значения переменной пути. Итак, если наш шаблон похож на“/\{example:[a-z]+}”,, значением переменной пути“example” будет любой сегмент пути, который соответствует регулярному выражению give:

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

@Test
public void whenGetRegexInPathVarible_thenGotPathVariable()
  throws Exception {

      client.get()
        .uri("/abcd")
        .exchange()
        .expectStatus()
        .isOk()
        .expectBody(String.class)
        .isEqualTo("/{example:[a-z]+} was accessed and "
          + "example=abcd");
}

3.5. ‘/{var1} {var2} '_ Переменные с несколькими путями в одном сегменте пути

Spring 5 удостоверился, что несколько переменных пути будут разрешены в одном сегменте пути, только если они разделены разделителем. Только тогда Spring сможет различить две разные переменные пути:

private RouterFunction 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("/example_tutorial")
        .exchange()
        .expectStatus()
        .isOk()
        .expectBody(String.class)
        .isEqualTo("example , tutorial");
}

4. Заключение

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

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