[Spring] @RequestMapping의 wildcard 패턴 **의 값을 배열로 얻기
안녕하세요, 하마연구소입니다.
스프링 프레임워크를 이용하여 컨트롤러에 패턴을 등록하면 다양한 URI로 매핑시킬 수 있습니다.
이때 와일드카드(wildcard)를 자주 사용하게 되는데 기본적으로 ?, *, ** 가 있습니다.
아래는 스프링 문서 설명입니다.
※ 출처: Spring Web MVC - Web on Servlet Stack (https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html)
또한, {XXXXX}과 @PathVariable 어노테이션을 이용하여 URI의 특정부분을 변수에 할당할 수도 있습니다.
그리고 정규표현식(regular expression)도 지원합니다.
가끔 매핑 패턴을 /path1/path2/** 처럼 여러개의 경로를 지원할 필요가 있는데, 단순하게 매핑뿐만 아니라 실제 ** 에 해당하는 경로를 얻어야할 때가 있습니다.
** 에 해당하는 값을 @PathVariable 처럼 문자열(String) 또는 문자열배열(String[])로 쉽게 바인딩하는 것을 스프링에서 지원하면 좋겠지만, 아무리 찾아봐도 없더군요.
그래서 이것을 구현하기 위한 방법을 설명하려고 합니다.
혹시라도 스프링 기본기능으로 바인딩하는 방법을 아시는 분은 댓글로 남겨주세요.
아래와 같이 컨트롤러에 @WildcardPathVariable 어노테이션을 사용하여 **에 해당하는 값을 문자열배열로 바인딩하려고 합니다.
스프링 프레임워크를 이용하여 컨트롤러에 패턴을 등록하면 다양한 URI로 매핑시킬 수 있습니다.
이때 와일드카드(wildcard)를 자주 사용하게 되는데 기본적으로 ?, *, ** 가 있습니다.
아래는 스프링 문서 설명입니다.
※ 출처: Spring Web MVC - Web on Servlet Stack (https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html)
또한, {XXXXX}과 @PathVariable 어노테이션을 이용하여 URI의 특정부분을 변수에 할당할 수도 있습니다.
그리고 정규표현식(regular expression)도 지원합니다.
가끔 매핑 패턴을 /path1/path2/** 처럼 여러개의 경로를 지원할 필요가 있는데, 단순하게 매핑뿐만 아니라 실제 ** 에 해당하는 경로를 얻어야할 때가 있습니다.
** 에 해당하는 값을 @PathVariable 처럼 문자열(String) 또는 문자열배열(String[])로 쉽게 바인딩하는 것을 스프링에서 지원하면 좋겠지만, 아무리 찾아봐도 없더군요.
그래서 이것을 구현하기 위한 방법을 설명하려고 합니다.
혹시라도 스프링 기본기능으로 바인딩하는 방법을 아시는 분은 댓글로 남겨주세요.
아래와 같이 컨트롤러에 @WildcardPathVariable 어노테이션을 사용하여 **에 해당하는 값을 문자열배열로 바인딩하려고 합니다.
@Controller @RequestMapping(value = "/test") public class TestController { @GetMapping("/path1/path2/**") public String[] getPaths(@WildcardPathVariable String[] paths) { return paths; } }
아래는 @WildcardPathVariable 어노테이션 구현체입니다.
아래처럼 @WildcardPathVariable이 작동하도록 resolver를 작성합니다.
핵심은 스프링에서 Request 객체에 attribute로 설정해주는 값값 중, HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE 과 HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE 을 얻는것과, AntPathMatcher를 사용하는 것입니다.
특히 AntPathMatcher의 extractPathWithinPattern() 메서드가 중요합니다.
이 메서드를 간단하게 설명하면, 입력된 패턴(pattern) 중 wildcard 위치에 해당하는 경로(path)를 반환합니다.
자세한 것은 스프링 소스코드 주석에 있는 샘플을 참고해주세요.
Resolver를 사용하기 위하여 빈으로 정의하고, 스프링 argumentResolver에 등록합니다.
/** * 패턴이 "/path1/path2/**"이고 경로가 "/path1/path2/path3/path4"일 때, "path3/path4"를 얻기위한 어노테이션 */ @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface WildcardPathVariable { }
아래처럼 @WildcardPathVariable이 작동하도록 resolver를 작성합니다.
핵심은 스프링에서 Request 객체에 attribute로 설정해주는 값값 중, HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE 과 HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE 을 얻는것과, AntPathMatcher를 사용하는 것입니다.
특히 AntPathMatcher의 extractPathWithinPattern() 메서드가 중요합니다.
이 메서드를 간단하게 설명하면, 입력된 패턴(pattern) 중 wildcard 위치에 해당하는 경로(path)를 반환합니다.
자세한 것은 스프링 소스코드 주석에 있는 샘플을 참고해주세요.
/** * WildcardPathVariable 어노테이션을 처리하는 resolver */ public class WildcardPathVariableHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { /** * AntPathMatcher */ private AntPathMatcher antPathMatcher = new AntPathMatcher(); @Override public boolean supportsParameter(MethodParameter methodParameter) { return methodParameter.hasParameterAnnotation(WildcardPathVariable.class); } @Override public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception { HttpServletRequest httpServletRequest = (HttpServletRequest) nativeWebRequest.getNativeRequest(); // /path1/path2/** String pattern = (String) httpServletRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); // /path1/path2/path3/path4 String path = (String) httpServletRequest.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE); // 와일드카드 이후로 얻기 String wildcardPath = antPathMatcher.extractPathWithinPattern(pattern, path); // "/"으로 분리 String[] splitPaths = StringUtils.split(wildcardPath, AntPathMatcher.DEFAULT_PATH_SEPARATOR); return splitPaths; } }
Resolver를 사용하기 위하여 빈으로 정의하고, 스프링 argumentResolver에 등록합니다.
@Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(wildcardPathVariableHandlerMethodArgumentResolver()); } @Bean public WildcardPathVariableHandlerMethodArgumentResolver wildcardPathVariableHandlerMethodArgumentResolver() { return new WildcardPathVariableHandlerMethodArgumentResolver(); } }
감사합니다.
댓글
댓글 쓰기