Spring에서 URL의 PathVariable을 Filter 단계에서 호출하여 사용하기
@PathVariable
변수의 사용
Spring의 RequestMapping은 다음과 같이 고정 값이 아닌 path variable을 지정하여 사용할 수 있다.
@GetMapping("/{projectId}/setting/{mainMenuId}/{subMenuId}")
public String page(@PathVariable String projectId, @PathVariable mainMenuId, @PathVariable String subMenuId) {
// ... 생략
}
이렇게 설정되는 PathVariable
은 HandlerMethodArgumentResolver
중 PathVariableMethodArgumentResolver
를 통해 requestAttribute에 해당 값 들이 추가되어 Java에서 다음처럼 호출하여 사용할 수 있다.
var projectId = RequestContextHolder.currentRequestAttributes().getAttribute("projectId", RequestAttributes.SCOPE_REQUEST);
Thymeleaf의 경우 다음처럼 호출하여 사용할 수 있다.
<div th:text="${projectId}"></div>
그런데 filter에서는 requestAttribute를 호출하여 사욜 할 수 없다.HandlerMethodArgumentResolver
가 동작하는 구간이 Filter 동작 이후 이기 때문이다.
PathPatternParser
사용하기
Filter 구간에서 pathVariable을 사용하는 방법을 알아보기에 앞서 우선 pathVariable을 획득하는 방법을 알아보면 Spring은 URI path pattern 구문을 분석하기 위해 PathPatternParser
를 Spring Framework 5.0 이후 제공한다.
PathPatternParser
를 사용하면 다음과 같이 pathVariable을 획득할 수 있다.
@Test
void pathPatternTest() {
var pathPatternParser = new PathPatternParser();
PathPattern pathPattern = pathPatternParser.parse("/{projectId}/setting/{mainMenuId}/{subMenuId}");
PathContainer path = PathContainer.parsePath( "/admin/setting/menu/query");
PathMatchInfo matchAndExtract = pathPattern.matchAndExtract(path);
System.out.println("result : " + matchAndExtract);
// 결과 : PathMatchInfo[uriVariables={mainMenuId=menu, subMenuId=query, projectId=admin}, matrixVariables={}]
}
RequestMappingHandlerMapping
에 등록된 주소를 찾아 현재 요청에 대한 pathVariable 획득하기
요청 시 주소는 매번 변경되기 때문에 위 예제처럼 고정된 주소를 기준으로 PathPatternParser
parse를 할 수 없다.
Spring은 application 실행 시 @RequestMapping
등을 통해 등록한 RequestMapping 정보를 RequestMappingHandlerMapping
에 모두 담아두고 Request 마다 해당 요청 URI에 해당하는 등록되어 있는 가장 최적의 RequstMapping을 반환한다.
따라서 요청 시 RequestMapping 정보에서 현재 요청에 해당하는 등록된 pathPattern을 획득하여 현재 요청에 대한 pathVariable을 획득하는게 좋다.
다음이 그 예이다.
public static Map<String, String> getUriVariableMap() {
var applicationContext = ApplicationContextUtil.getApplicationContext();
RequestMappingHandlerMapping mapping = applicationContext.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HandlerExecutionChain handler = mapping.getHandler(request);
if(handler == null) return null;
var handlerMethod = (HandlerMethod) handler.getHandler();
Map<RequestMappingInfo, HandlerMethod> handlerMethods = mapping.getHandlerMethods();
RequestMappingInfo requestMappingInfo = null;
for(var entry : handlerMethods.entrySet()) {
if (entry.getValue().getMethod() == handlerMethod.getMethod()) {
requestMappingInfo = entry.getKey();
}
}
PathContainer path = ServletRequestPathUtils.getParsedRequestPath(request).pathWithinApplication();
PathPattern pathPattern = requestMappingInfo.getPathPatternsCondition().getFirstPattern();
PathMatchInfo matchAndExtract = pathPattern.matchAndExtract(path);
return matchAndExtract.getUriVariables();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
위 예제에서는 ApplicationContext를 개인적으로 따로 만들어둔 ApplicationContextUtil에서 획득하여 사용하였다.
적절하게 applicationContext를 호출하여 사용하면 된다.
Filter 단계에서는 현재 요청에 해당하는 pathVariable은 위와 같은 방식으로 호출하여 사용하면 된다.