본문 바로가기
spring

Custom HandlerMethodArgumentResolver

by 쭈꾸마뇽 2021. 6. 8.

HandlerMethodArgumentResolver 

HandlerMethodArgumentResolver는 API가 호출될 때 매개 변수를 입력받기 위해 사용하는 Spring에서 제공하는 기능이다.  해당 인터페이스로 가보면 "지정된 요청의 컨텍스트에서 메서드 매개 변수를 인수 값으로 만들기 위한 전략 인터페이스" 라고 설명이 되있다.  

즉 우리가 Controller에서 매개 변수를 받기 위해 사용하는 @PathVariable, @RequestParam, @RequestBody 등이 이 인터페이스를 이용해 원하는 값을 얻게 해주는 것이다.  

 

HandlerMethodArgumentResolver는 두개의 추상 메소드를 가지고 있다. 

  • supportsParameter : 이 Resolver가 지원하는 매개 변수인 경우 true를 리턴하고 그 외에는 false를 리턴한다
  • resulveArgument : 요청 데이터에서 찾아서 매개 변수의 값에 매핑해 리턴해준다

Custom HandlerMethodArgumentResolver 만들기

직접 HandlerMethodArgumentResolver를 만들어 사용하려면 받고자 하는 인자값에 명시해 줄 Annotation을 만들어줘야 한다.  먼저 API를 호출한 사람의 IP를 얻는 기능을 추가해보겠다.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface GetIp {
}
public class GetIpArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterAnnotation(GetIp.class) != null;
    }

    @Override
    public Object resolveArgument(
            MethodParameter parameter,
            ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest,
            WebDataBinderFactory binderFactory
    ) {
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        String ip = request.getHeader("X-FORWARDED-FOR");
        if (ip == null) ip = request.getRemoteAddr();

        return ip;
    }
}

supportsParameter 함수에는 우리가 선언한 Annotation이 적용된지 판별하는 판별식을 작성해준다.  

resolveArgument에는 webRequest로부터 ip를 얻기 위해 HttpServletRequest로 변환해 준뒤 ip를 보관하는 헤더값을 가져와 리턴해준다.

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(ipArgsResolver());
        resolvers.add(headerArgsResolver());
    }

    @Bean
    public GetIpArgumentResolver ipArgsResolver() {
        return new GetIpArgumentResolver();
    }
}

마지막으로 직접 만든 Resolver를 Bean에 등록해주고 Controller에서 받고자하는 매개변수에 Annotation을 붙여준다.

@RestController
public class MyController {

    @GetMapping("/getIp")
    public Map<String, ?> getIp(@GetIp String ip) {
        return Map.of(
                "result", ip
        );
    }
}

HandlerMethodArgumentResolver는 한개가 아니라 여러개도 등록해 사용할 수 있다.  이번에는 Header에 저장된 모든 값을 가져오는 Resolver를 만들어 보겠다.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface GetHeader {
}
public class GetHeaderArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterAnnotation(GetHeader.class) != null;
    }

    @Override
    public Object resolveArgument(
            MethodParameter parameter,
            ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest,
            WebDataBinderFactory binderFactory
    ) throws Exception {
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        Enumeration<String> headerNames = request.getHeaderNames();
        Map<String, String> headers = new HashMap<>();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            headers.put(headerName, request.getHeader(headerName));
        }

        return headers;
    }
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(ipArgsResolver());
        resolvers.add(headerArgsResolver());
    }

    @Bean
    public GetIpArgumentResolver ipArgsResolver() {
        return new GetIpArgumentResolver();
    }

    @Bean
    public GetHeaderArgumentResolver headerArgsResolver() {
        return new GetHeaderArgumentResolver();
    }
}
@RestController
public class MyController {

    @GetMapping("/getIp")
    public Map<String, ?> getIp(@GetIp String ip) {
        return Map.of(
                "result", ip
        );
    }

    @GetMapping("/getHeader")
    public Map<String, ?> getHeader(@GetHeader Object headers) {
        return Map.of(
                "result", headers
        );
    }
}

같은 방법으로 매개 변수에 붙여줄 Annotation을 만들어 주고 Resolver를 만든다음 Bean에 등록하여 사용하였다.

결과값으로 모든 Header 정보들이 리턴되는 것을 확인할 수 있다.

댓글