Loading...
Spring Framework Reference Documentation 7.0.2의 URI Links의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
See equivalent in the Reactive stack
이 섹션에서는 Spring Framework에서 URI를 다루기 위해 제공되는 다양한 옵션을 설명합니다.
Spring MVC and Spring WebFlux
UriComponentsBuilder는 다음 예제에서 보이는 것처럼 변수와 함께 URI 템플릿으로부터 URI를 생성하는 데 도움이 됩니다:
1UriComponents uriComponents = UriComponentsBuilder 2 .fromUriString("https://example.com/hotels/{hotel}") // (1) 3 .queryParam("q", "{q}") // (2) 4 .encode() // (3) 5 .build(); // (4) 6 7URI uri = uriComponents.expand("Westin", "123").toUri(); // (5)
| 1 | URI 템플릿을 사용하는 static 팩터리 메서드입니다. |
| 2 | URI 컴포넌트를 추가하거나 교체합니다. |
| 3 | URI 템플릿과 URI 변수이 인코딩되도록 요청합니다. |
| 4 | UriComponents를 생성합니다. |
| 5 | 변수를 expand하고 URI를 얻습니다. |
1val uriComponents = UriComponentsBuilder 2 .fromUriString("https://example.com/hotels/{hotel}") // (1) 3 .queryParam("q", "{q}") // (2) 4 .encode() // (3) 5 .build() // (4) 6 7val uri = uriComponents.expand("Westin", "123").toUri() // (5)
| 1 | URI 템플릿을 사용하는 static 팩터리 메서드입니다. |
| 2 | URI 컴포넌트를 추가하거나 교체합니다. |
| 3 | URI 템플릿과 URI 변수가 인코딩되도록 요청합니다. |
| 4 | UriComponents를 생성합니다. |
| 5 | 변수를 expand하고 URI를 얻습니다. |
앞의 예제는 다음 예제에서 보이는 것처럼 buildAndExpand를 사용하여 하나의 체인으로 합치고 단축할 수 있습니다:
1URI uri = UriComponentsBuilder 2 .fromUriString("https://example.com/hotels/{hotel}") 3 .queryParam("q", "{q}") 4 .encode() 5 .buildAndExpand("Westin", "123") 6 .toUri();
1val uri = UriComponentsBuilder 2 .fromUriString("https://example.com/hotels/{hotel}") 3 .queryParam("q", "{q}") 4 .encode() 5 .buildAndExpand("Westin", "123") 6 .toUri()
다음 예제에서 보이는 것처럼 URI로 바로 이동함으로써(이는 인코딩을 의미합니다) 더 간단하게 만들 수 있습니다:
1URI uri = UriComponentsBuilder 2 .fromUriString("https://example.com/hotels/{hotel}") 3 .queryParam("q", "{q}") 4 .build("Westin", "123");
1val uri = UriComponentsBuilder 2 .fromUriString("https://example.com/hotels/{hotel}") 3 .queryParam("q", "{q}") 4 .build("Westin", "123")
다음 예제에서 보이는 것처럼 전체 URI 템플릿을 사용하여 더 간단하게 만들 수 있습니다:
1URI uri = UriComponentsBuilder 2 .fromUriString("https://example.com/hotels/{hotel}?q={q}") 3 .build("Westin", "123");
1val uri = UriComponentsBuilder 2 .fromUriString("https://example.com/hotels/{hotel}?q={q}") 3 .build("Westin", "123")
Spring MVC and Spring WebFlux
UriComponentsBuilder는 UriBuilder를 구현합니다. 그에 따라 UriBuilderFactory를 사용하여
UriBuilder를 생성할 수 있습니다. UriBuilderFactory와 UriBuilder는 함께 base URL, 인코딩 선호도 및 기타 세부 정보와 같은
공유 설정을 기반으로 URI 템플릿으로부터 URI를 생성하기 위한 플러그형 메커니즘을 제공합니다.
RestTemplate과 WebClient를 UriBuilderFactory로 설정하여 URI 준비를 사용자 정의할 수 있습니다.
DefaultUriBuilderFactory는 내부적으로 UriComponentsBuilder를 사용하고
공유 설정 옵션을 노출하는 UriBuilderFactory의 기본 구현입니다.
다음 예제는 RestTemplate을 설정하는 방법을 보여줍니다:
1// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode; 2 3String baseUrl = "https://example.org"; 4DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl); 5factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES); 6 7RestTemplate restTemplate = new RestTemplate(); 8restTemplate.setUriTemplateHandler(factory);
1// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode 2 3val baseUrl = "https://example.org" 4val factory = DefaultUriBuilderFactory(baseUrl) 5factory.encodingMode = EncodingMode.TEMPLATE_AND_VALUES 6 7val restTemplate = RestTemplate() 8restTemplate.uriTemplateHandler = factory
다음 예제는 WebClient를 설정합니다:
1// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode; 2 3String baseUrl = "https://example.org"; 4DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl); 5factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES); 6 7WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
1// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode 2 3val baseUrl = "https://example.org" 4val factory = DefaultUriBuilderFactory(baseUrl) 5factory.encodingMode = EncodingMode.TEMPLATE_AND_VALUES 6 7val client = WebClient.builder().uriBuilderFactory(factory).build()
추가로, DefaultUriBuilderFactory를 직접 사용할 수도 있습니다. 이는 UriComponentsBuilder의 static 팩터리 메서드를 사용하는 것과
비슷하지만, static 팩터리 메서드 대신 설정과 선호도를 보유하는 실제 인스턴스를 사용한다는 점이 다릅니다.
다음 예제에서 보이는 것처럼 사용할 수 있습니다:
1String baseUrl = "https://example.com"; 2DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl); 3 4URI uri = uriBuilderFactory.uriString("/hotels/{hotel}") 5 .queryParam("q", "{q}") 6 .build("Westin", "123");
1val baseUrl = "https://example.com" 2val uriBuilderFactory = DefaultUriBuilderFactory(baseUrl) 3 4val uri = uriBuilderFactory.uriString("/hotels/{hotel}") 5 .queryParam("q", "{q}") 6 .build("Westin", "123")
Spring MVC and Spring WebFlux
UriComponentsBuilder는 두 가지 URI parser 타입을 지원합니다:
RFC parser — 이 parser 타입은 URI 문자열이 RFC 3986 문법을 준수할 것을 기대하며, 문법에서 벗어나는 경우를 illegal한 것으로 간주합니다.
WhatWG parser — 이 parser는 URL parsing algorithm에 기반하며, WhatWG URL Living standard에 정의되어 있습니다. 이는 예상치 못한 입력의 광범위한 경우에 대해 관대한 처리를 제공합니다. 브라우저는 사용자가 입력한 URL을 관대하게 처리하기 위해 이를 구현합니다. 자세한 내용은 URL Living Standard와 URL parsing test cases를 참조하십시오.
기본적으로 RestClient, WebClient, 그리고 RestTemplate는 RFC parser 타입을 사용하며,
애플리케이션이 RFC 문법을 준수하는 URL 템플릿을 제공할 것으로 기대합니다. 이를 변경하려면
클라이언트 중 하나에서 UriBuilderFactory를 사용자 정의할 수 있습니다.
애플리케이션과 프레임워크는 사용자 제공 URL을 parsing하여 scheme, host, port, path, query와 같은
URI 컴포넌트를 검사하고 필요하다면 검증하기 위해 자체적으로 UriComponentsBuilder에 의존할 수 있습니다.
이러한 컴포넌트는 URL을 보다 관대하게 처리하고, 입력 URL로 redirect하는 경우나
응답에 포함되어 브라우저로 전송되는 경우 브라우저가 URI를 parsing하는 방식과 맞추기 위해
WhatWG parser 타입을 사용하기로 결정할 수 있습니다.
Spring MVC and Spring WebFlux
UriComponentsBuilder는 두 수준의 인코딩 옵션을 노출합니다:
UriComponentsBuilder#encode(): 먼저 URI 템플릿을 pre-encode한 다음 expand 시 URI 변수를 엄격하게 인코딩합니다.
UriComponents#encode(): URI 변수가 expand된 후에 URI 컴포넌트를 인코딩합니다.
두 옵션 모두 non-ASCII 및 illegal character를 escaped octet으로 대체합니다. 그러나 첫 번째 옵션은 URI 변수에 나타나는 reserved meaning을 가진 character도 대체합니다.
path에서는 legal하지만 reserved meaning을 가지는 ";"를 고려해 보십시오. 첫 번째 옵션은 URI 변수에서<br>";"를 "%3B"로 대체하지만 URI 템플릿에서는 대체하지 않습니다. 반대로 두 번째 옵션은 path에서 legal한<br>character이기 때문에 ";"를 절대 대체하지 않습니다.
대부분의 경우 첫 번째 옵션이 예상되는 결과를 제공할 가능성이 높습니다. 이는 URI 변수를 완전히 인코딩해야 하는 opaque data로 취급하기 때문입니다. 반면 두 번째 옵션은 URI 변수에 의도적으로 reserved character가 포함된 경우에 유용합니다. 두 번째 옵션은 또한 URI 변수를 전혀 expand하지 않을 때도 유용한데, 이 경우 URI 변수처럼 보이는 모든 것도 인코딩되기 때문입니다.
다음 예제는 첫 번째 옵션을 사용합니다:
1URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}") 2 .queryParam("q", "{q}") 3 .encode() 4 .buildAndExpand("New York", "foo+bar") 5 .toUri(); 6 7// Result is "/hotel%20list/New%20York?q=foo%2Bbar"
1val uri = UriComponentsBuilder.fromPath("/hotel list/{city}") 2 .queryParam("q", "{q}") 3 .encode() 4 .buildAndExpand("New York", "foo+bar") 5 .toUri() 6 7// Result is "/hotel%20list/New%20York?q=foo%2Bbar"
다음 예제에서 보이는 것처럼 URI로 바로 이동함으로써(이는 인코딩을 의미합니다) 앞의 예제를 단축할 수 있습니다:
1URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}") 2 .queryParam("q", "{q}") 3 .build("New York", "foo+bar");
1val uri = UriComponentsBuilder.fromPath("/hotel list/{city}") 2 .queryParam("q", "{q}") 3 .build("New York", "foo+bar")
다음 예제에서 보이는 것처럼 전체 URI 템플릿을 사용하여 더 간단하게 만들 수 있습니다:
1URI uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}") 2 .build("New York", "foo+bar");
1val uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}") 2 .build("New York", "foo+bar")
WebClient와 RestTemplate은 UriBuilderFactory strategy를 통해 내부적으로 URI 템플릿을 expand하고 인코딩합니다.
둘 다 다음 예제에서 보이는 것처럼 커스텀 strategy로 설정할 수 있습니다:
1String baseUrl = "https://example.com"; 2DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl); 3factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES); 4 5// Customize the RestTemplate.. 6RestTemplate restTemplate = new RestTemplate(); 7restTemplate.setUriTemplateHandler(factory); 8 9// Customize the WebClient.. 10WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
1val baseUrl = "https://example.com" 2val factory = DefaultUriBuilderFactory(baseUrl).apply { 3 encodingMode = EncodingMode.TEMPLATE_AND_VALUES 4} 5 6// Customize the RestTemplate.. 7val restTemplate = RestTemplate().apply { 8 uriTemplateHandler = factory 9} 10 11// Customize the WebClient.. 12val client = WebClient.builder().uriBuilderFactory(factory).build()
DefaultUriBuilderFactory 구현은 내부적으로 UriComponentsBuilder를 사용하여
URI 템플릿을 expand하고 인코딩합니다. factory로서, 아래 인코딩 mode 중 하나를 기반으로
인코딩 방식에 대한 설정을 한 곳에서 제공할 수 있습니다:
TEMPLATE_AND_VALUES: UriComponentsBuilder#encode()를 사용하며, 이는 앞의 목록에서 첫 번째 옵션에 해당하며,
URI 템플릿을 pre-encode하고 expand 시 URI 변수를 엄격하게 인코딩합니다.
VALUES_ONLY: URI 템플릿을 인코딩하지 않고 대신 UriUtils#encodeUriVariables를 통해 URI 변수에
엄격한 인코딩을 적용한 후 템플릿에 expand합니다.
URI_COMPONENT: UriComponents#encode()를 사용하며, 이는 앞의 목록에서 두 번째 옵션에 해당하며,
URI 변수가 expand된 후에 URI 컴포넌트 값을 인코딩합니다.
NONE: 인코딩을 적용하지 않습니다.
RestTemplate은 역사적 이유와 이전 버전과의 호환성을 위해 EncodingMode.URI_COMPONENT로 설정되어 있습니다.
WebClient는 DefaultUriBuilderFactory의 기본 값을 사용하며, 이는 5.0.x에서는
EncodingMode.URI_COMPONENT였으나 5.1에서는 EncodingMode.TEMPLATE_AND_VALUES로 변경되었습니다.
다음 예제에서 보이는 것처럼 ServletUriComponentsBuilder를 사용하여 현재 요청을 기준으로 하는 URI를 생성할 수 있습니다:
1HttpServletRequest request = ... 2 3// Re-uses scheme, host, port, path, and query string... 4 5URI uri = ServletUriComponentsBuilder.fromRequest(request) 6 .replaceQueryParam("accountId", "{id}") 7 .build("123");
1val request: HttpServletRequest = ... 2 3// Re-uses scheme, host, port, path, and query string... 4 5val uri = ServletUriComponentsBuilder.fromRequest(request) 6 .replaceQueryParam("accountId", "{id}") 7 .build("123")
다음 예제에서 보이는 것처럼 context path를 기준으로 하는 URI를 생성할 수 있습니다:
1HttpServletRequest request = ... 2 3// Re-uses scheme, host, port, and context path... 4 5URI uri = ServletUriComponentsBuilder.fromContextPath(request) 6 .path("/accounts") 7 .build() 8 .toUri();
1val request: HttpServletRequest = ... 2 3// Re-uses scheme, host, port, and context path... 4 5val uri = ServletUriComponentsBuilder.fromContextPath(request) 6 .path("/accounts") 7 .build() 8 .toUri()
다음 예제에서 보이는 것처럼 Servlet(예: /main/*)을 기준으로 하는 URI를 생성할 수 있습니다:
1HttpServletRequest request = ... 2 3// Re-uses scheme, host, port, context path, and Servlet mapping prefix... 4 5URI uri = ServletUriComponentsBuilder.fromServletMapping(request) 6 .path("/accounts") 7 .build() 8 .toUri();
1val request: HttpServletRequest = ... 2 3// Re-uses scheme, host, port, context path, and Servlet mapping prefix... 4 5val uri = ServletUriComponentsBuilder.fromServletMapping(request) 6 .path("/accounts") 7 .build() 8 .toUri()
5.1부터
ServletUriComponentsBuilder는 클라이언트 기원 주소를 지정하는Forwarded및<br>X-Forwarded-*헤더의 정보를 무시합니다. 이러한 헤더를 추출하여 사용하거나 폐기하기 위해<br>ForwardedHeaderFilter를 사용하는 것을 고려하십시오.
Spring MVC는 컨트롤러 메서드에 대한 링크를 준비하는 메커니즘을 제공합니다. 예를 들어, 다음 MVC 컨트롤러는 링크 생성을 허용합니다:
1@Controller 2@RequestMapping("/hotels/{hotel}") 3public class BookingController { 4 5 @GetMapping("/bookings/{booking}") 6 public ModelAndView getBooking(@PathVariable Long booking) { 7 // ... 8 } 9}
1@Controller 2@RequestMapping("/hotels/{hotel}") 3class BookingController { 4 5 @GetMapping("/bookings/{booking}") 6 fun getBooking(@PathVariable booking: Long): ModelAndView { 7 // ... 8 } 9}
다음 예제에서 보이는 것처럼 메서드 이름을 참조하여 링크를 준비할 수 있습니다:
1UriComponents uriComponents = MvcUriComponentsBuilder 2 .fromMethodName(BookingController.class, "getBooking", 21).buildAndExpand(42); 3 4URI uri = uriComponents.encode().toUri();
1val uriComponents = MvcUriComponentsBuilder 2 .fromMethodName(BookingController::class.java, "getBooking", 21).buildAndExpand(42) 3 4val uri = uriComponents.encode().toUri()
앞의 예제에서는 URL에 삽입될 path 변수로 사용하기 위해 실제 메서드 인자 값(이 경우 long 값: 21)을 제공합니다.
또한 type-level 요청 매핑에서 상속된 hotel 변수와 같이 남아 있는 URI 변수를 채우기 위해
값 42를 제공합니다. 메서드에 인자가 더 있다면 URL에 필요하지 않은 인자에는 null을 제공할 수 있습니다.
일반적으로 URL을 구성하는 데는 @PathVariable과 @RequestParam 인자만 관련이 있습니다.
MvcUriComponentsBuilder를 사용하는 추가적인 방법이 있습니다. 예를 들어, 다음 예제에서 보이는 것처럼
프록시를 통한 mock 테스트와 유사한 기술을 사용하여 컨트롤러 메서드 이름을 직접 참조하지 않을 수 있습니다
(예제는 MvcUriComponentsBuilder.on의 static import를 가정합니다):
1UriComponents uriComponents = MvcUriComponentsBuilder 2 .fromMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42); 3 4URI uri = uriComponents.encode().toUri();
1val uriComponents = MvcUriComponentsBuilder 2 .fromMethodCall(on(BookingController::class.java).getBooking(21)).buildAndExpand(42) 3 4val uri = uriComponents.encode().toUri()
컨트롤러 메서드 시그니처는
fromMethodCall을 사용하여 링크 생성을 할 수 있어야 할 때 설계에 제한이 있습니다.<br>적절한 파라미터 시그니처가 필요할 뿐 아니라, 반환 타입에 대한 기술적 제약(즉, 링크 빌더 호출에 대해<br>런타임 프록시를 생성해야 함)이 있어 반환 타입은final이면 안 됩니다. 특히 view 이름에 대한 일반적인<br>String반환 타입은 여기서 동작하지 않습니다. 대신ModelAndView또는 (반환 값이String인) 단순한<br>Object를 사용해야 합니다.
앞의 예제는 MvcUriComponentsBuilder의 static 메서드를 사용합니다. 내부적으로 이들은
현재 요청의 scheme, host, port, context path, servlet path로부터 base URL을 준비하기 위해
ServletUriComponentsBuilder에 의존합니다. 이는 대부분의 경우 잘 동작합니다.
그러나 때로는 이것으로 충분하지 않을 수 있습니다. 예를 들어, 요청 컨텍스트 밖에 있거나(예: 링크를 준비하는 batch 프로세스)
요청 path에서 제거된 locale prefix를 다시 링크에 삽입해야 하는 경우가 있을 수 있습니다.
이러한 경우, base URL로 사용할 UriComponentsBuilder를 받는 static fromXxx 오버로드 메서드를 사용할 수 있습니다.
또는 base URL을 사용하여 MvcUriComponentsBuilder 인스턴스를 생성한 다음 인스턴스 기반 withXxx 메서드를 사용할 수 있습니다.
예를 들어, 다음 listing은 withMethodCall을 사용합니다:
1UriComponentsBuilder base = ServletUriComponentsBuilder.fromCurrentContextPath().path("/en"); 2MvcUriComponentsBuilder builder = MvcUriComponentsBuilder.relativeTo(base); 3UriComponents uriComponents = builder.withMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42); 4 5URI uri = uriComponents.encode().toUri();
1val base = ServletUriComponentsBuilder.fromCurrentContextPath().path("/en") 2val builder = MvcUriComponentsBuilder.relativeTo(base) 3val uriComponents = builder.withMethodCall(on(BookingController::class.java).getBooking(21)).buildAndExpand(42) 4 5val uri = uriComponents.encode().toUri()
5.1부터
MvcUriComponentsBuilder는 클라이언트 기원 주소를 지정하는Forwarded및<br>X-Forwarded-*헤더의 정보를 무시합니다. 이러한 헤더를 추출하여 사용하거나 폐기하기 위해<br>ForwardedHeaderFilter를 사용하는 것을 고려하십시오.
Thymeleaf, FreeMarker, JSP와 같은 view에서 각 요청 매핑에 묵시적 또는 명시적으로 할당된 이름을 참조하여 annotation 기반 컨트롤러에 대한 링크를 생성할 수 있습니다.
다음 예제를 고려해 보십시오:
1@RequestMapping("/people/{id}/addresses") 2public class PersonAddressController { 3 4 @RequestMapping("/{country}") 5 public HttpEntity<PersonAddress> getAddress(@PathVariable String country) { ... } 6}
1@RequestMapping("/people/{id}/addresses") 2class PersonAddressController { 3 4 @RequestMapping("/{country}") 5 fun getAddress(@PathVariable country: String): HttpEntity<PersonAddress> { ... } 6}
앞의 컨트롤러를 기준으로, JSP에서 다음과 같이 링크를 준비할 수 있습니다:
1<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> 2... 3<a href="${s:mvcUrl('PAC#getAddress').arg(0,'US').buildAndExpand('123')}">Get Address</a>
앞의 예제는 Spring 태그 라이브러리(즉, META-INF/spring.tld)에 선언된 mvcUrl function에 의존하지만,
자신만의 function을 정의하거나 다른 템플릿 기술을 위해 유사한 function을 준비하는 것은 쉽습니다.
작동 방식은 다음과 같습니다. 시작 시, 모든 @RequestMapping은
HandlerMethodMappingNamingStrategy를 통해 기본 이름이 할당되며, 기본 구현은
클래스와 메서드 이름의 대문자를 사용합니다(예: ThingController의 getThing 메서드는 "TC#getThing"이 됨).
이름 충돌이 있는 경우 @RequestMapping(name="..")을 사용하여 명시적인 이름을 할당하거나
자신만의 HandlerMethodMappingNamingStrategy를 구현할 수 있습니다.
Functional Endpoints
Asynchronous Requests