파란하늘의 지식창고
반응형

@PathVariable 변수의 사용

Spring의 RequestMapping은 다음과 같이 고정 값이 아닌 path variable을 지정하여 사용할 수 있다.

@GetMapping("/{projectId}/setting/{mainMenuId}/{subMenuId}")
public String page(@PathVariable String projectId, @PathVariable mainMenuId, @PathVariable String subMenuId) {
    // ... 생략
}

이렇게 설정되는 PathVariableHandlerMethodArgumentResolverPathVariableMethodArgumentResolver 를 통해 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은 위와 같은 방식으로 호출하여 사용하면 된다.

반응형
profile

파란하늘의 지식창고

@Bluesky_

도움이 되었다면 광고를 클릭해주세요