1. 개요

Spring 5는  URI 템플릿 패턴을 구문 분석하기 위한 새로운 PathPatternParser 를 가져왔습니다 . 이것은 이전에 사용된 AntPathMatcher 의 대안 입니다.

AntPathMatcher 는 Ant 스타일 경로 패턴 일치를 구현한 것입니다 . PathPatternParser 는 경로를 PathElements 의 연결된 List으로 나눕니다 . PathElements 체인은 빠른 패턴 일치를 위해 PathPattern 클래스에서 사용합니다.

PathPatternParser 와 함께 새로운 URI 변수 구문에 대한 지원도 도입되었습니다.

이 기사에서는 Spring 5.0 WebFlux에 도입된 신규/업데이트된 URL 패턴 매처 와 이전 버전의 Spring부터 있었던 것을 살펴보겠습니다 .

2. Spring 5.0의 새로운 URL 패턴 매처

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 givenHandlerMethod_whenMultipleURIVariablePattern_then200() {
        
    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 사용

RouterFunction 을 사용하는 새로운 URI 변수 경로 패턴의 예를 살펴보겠습니다 .

private RouterFunction<ServerResponse> routingFunction() {
    return route(GET("/test/{*id}"), 
      serverRequest -> ok().body(fromValue(serverRequest.pathVariable("id"))));
}

이 경우 "/test" 뒤에 작성하는 모든 경로는 경로 변수 "id"에 캡처됩니다. 따라서 이에 대한 테스트 사례는 다음과 같습니다.

@Test
public void givenRouter_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.txttest.txt 에 각각 "hello""test" 가 포함되어 있다고 가정해 보겠습니다 . 이는 JUnit 테스트 케이스로 시연할 수 있습니다.

@Test 
public void givenResources_whenAccess_thenGot() 
  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 패턴 매처를 살펴보겠습니다. 이러한 모든 패턴은 @GetMapping을 사용하는 RouterFunction 및 Handler 메서드 모두 에서 작동 합니다 .

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 givenRouter_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" 로 지정하면 "/baeldung/Id", "/baeldung/tutorialId", "/baeldung/articleId" 등과 같은 경로 패턴과 일치합니다 .

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

@Test
public void givenRouter_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/" 뒤의 모든 경로가 임의 개수의 경로 세그먼트와 일치합니다 .

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

@Test
public void givenRouter_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]+}' 정규식

경로 변수 값에 대한 정규식을 지정할 수도 있습니다. 따라서 패턴이 "/{baeldung:[az]+}"와 같은 경우 경로 변수 "baeldung" 의 값 은 정규식과 일치하는 모든 경로 세그먼트가 됩니다.

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 givenRouter_whenGetRegexInPathVarible_thenGotPathVariable() 
  throws Exception {
 
      client.get()
        .uri("/abcd")
        .exchange()
        .expectStatus()
        .isOk()
        .expectBody(String.class)
        .isEqualTo("/{baeldung:[a-z]+} was accessed and "
          + "baeldung=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 givenRouter_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 에서 찾을 수 있습니다 .

Generic footer banner