-
[WebFlux] query string에 담긴 String 파라미터를 localDate로 캐스팅하고 매핑하기웹 개발/WebFlux 2019. 12. 23. 14:22
get 방식 query string으로 파라미터를 넘겼을 때, controller에서 파라미터로 받는 object에 속한 field의 localDate 타입을 캐스팅하고 매핑해주기
이 방법을 찾게 된 계기는 클라이언트단에서 넘겨줄 파라미터가 많아서 하나하나 받으면 코드가 더러워질 것 같아 dto로 하나의 클래스를 만들기로 했다.
단순히 localDate 타입이 되어야하는 딱 하나의 파라미터를 받는다면, 아래처럼 짜면 쉽게 받아와졌다. (@DateTimeFormat을 이용하면 된다)
하나의 localDate형식의 파라미터를 받고 싶을 때,
// 요청 url: http://localhost:8080/festivals?eventStartDate=2010-10-01 @GetMapping("/festivals") fun getFestival( @RequestParam("eventStartDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) eventStartDate: LocalDate ) { // do something.. }
하지만 위에서 말했듯이 넘어와야할 파라미터가 많았으므로 DTO를 하나 만들었다.
여러개의 파라미터를 mapping할 DTO
class FestivalSearchDto( val listYN: String? = "", val arrage: String? = "", val areaCode: String? = "", val sigunguCode: String? = "", val eventStartDate: LocalDate?, val eventEndDate: LocalDate?, val numOfRows: Int? = 10, val pageNo: Int? = 1, val mobileOS: String, val mobileApp: String, val _type: String = "json" )
query string으로 전달한 요청 url
// 요청 url http://localhost:8080/festivals?listYN=Y&arrage=A&areaCode=32&eventStartDate=2020-01-11&eventEndDate=2020-01-29&numOfRows=12&pageNo=1&mobileOS=ETC&mobileApp=TestApp
요청 url을 받을 api 부분
@GetMapping("/festivals") fun getFestival(festivalSearchDto: FestivalSearchDto): Mono<List<FestivalInfo>> { log.debug("축제 정보 api 파라미터 festivalSearchDto 정보 확인 $festivalSearchDto") return festivalService.getFestivalInfos(festivalSearchDto) }
축제 정보 api가 정상작동하는지 작성한 테스트 코드
@Test fun `축제 정보 url 테스트`() { val builder = UriComponentsBuilder.fromUriString("/festivals") .queryParam("listYN", "Y") .queryParam("arrage", "A") .queryParam("areaCode", AreaCode.GANGWON.areaCode) .queryParam("eventStartDate", "2020-01-11") .queryParam("eventEndDate", "2020-01-29") .queryParam("numOfRows", 12) .queryParam("pageNo", 1) .queryParam("mobileOS", "ETC") .queryParam("mobileApp", "TestApp") .build(false) val entity = restTemplate.getForEntity( builder.toUriString(), List::class.java ) assertThat(entity.statusCode).isEqualTo(HttpStatus.OK) assertThat(entity.body).isNotEmpty println(">>축제 정보 url 테스트 결과 : $entity") }
아....... 그래 생각처럼 잘될리가 없지 ...
에러 로그
{ "timestamp": "2019-12-22T17:50:21.761+0000", "path": "/festivals", "status": 400, "error": "Bad Request", "message": "Validation failed for argument at index 0 in method: public reactor.core.publisher.Mono<java.util.List<com.festival.model.FestivalInfo>> com.festival.controller.FestivalController.getFestival(com.festival.model.dto.FestivalSearchDto), with 2 error(s): [Field error in object 'festivalSearchDto' on field 'eventEndDate': rejected value [2020-01-29]; codes [typeMismatch.festivalSearchDto.eventEndDate,typeMismatch.eventEndDate,typeMismatch.java.time.LocalDate,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [festivalSearchDto.eventEndDate,eventEndDate]; arguments []; default message [eventEndDate]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDate' for property 'eventEndDate'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDate] for value '2020-01-29'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2020-01-29]]] [Field error in object 'festivalSearchDto' on field 'eventStartDate': rejected value [2020-01-11]; codes [typeMismatch.festivalSearchDto.eventStartDate,typeMismatch.eventStartDate,typeMismatch.java.time.LocalDate,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [festivalSearchDto.eventStartDate,eventStartDate]; arguments []; default message [eventStartDate]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDate' for property 'eventStartDate'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDate] for value '2020-01-11'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2020-01-11]]] ",
Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDate'
String을 LocalDate로 converting 할 수 없단다.....!
삽질을 얼마나 했던가.... DTO의 field에 @DateTimeFormat을 붙여보고, Json 형식을 매핑 문제인가, 직렬화 문제인가 이것저것 다 붙여봤는데, 계속 실패 실패 실패!!!
자... 처음부터 차근차근 구글링을 해봅시다.
첫번째로 스택오버플로를 타고 들어가서 단서를 찾았다.
spring/spring boot는 json Body 형태(보통 post 방식에서 사용)에만 date type을 처리한다. 그렇다는 건, query string에 대한 date type을 처리해주지 않는다.
(spring에서는 json 데이터를 처리해주는 라이브러리 jackson이 있쥬)
그래서 필드에 @DateTimeFormat을 붙였던건데, object의 필드에는 @DateTimeFormat을 선언해도 formatting이 되지 않나보다.
내 문제를 해결하려면 request parameter인 query string을 date type으로 handling 할 수 있는 설정이 필요하다.
해결법은 있었지만, mvcConfigurer로 설명되어 있었다. 난 mvc가 아니라 webFlux를 사용하고 있었으므로 webFlux로 설정을 맞춰야했고, reference를 참고해보니, 내가 원하는 추가 설정은 mvc에서 해주는거나 차이가 없었다. (둘 다 mvc, webflux 설정을 커스터마이징 해주는 설정이었다)
해결한 방법: IsoFormat 방식을 사용하도록 configuration 추가 및 변경
@Configuration @EnableWebFlux class WebConfiguration : WebFluxConfigurer { // query string으로 넘어오는 string을 date(localDate, localDateTime)타입으로 casting 해줌 override fun addFormatters(registry: FormatterRegistry) { val registrar = DateTimeFormatterRegistrar() registrar.setUseIsoFormat(true) registrar.registerFormatters(registry) } }
위의 설정을 추가하니 깔끔하게 테스트 통과
출처:
- StackOverFlow - How to use LocalDateTime RequestParam in Spring? I get “Failed to convert String to LocalDateTime”
- https://github.com/swagger-api/swagger-codegen/issues/4113
- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/config/WebFluxConfigurer.html
'웹 개발 > WebFlux' 카테고리의 다른 글
[WebClient] Content type 'application/octet-stream' not supported for bodyType='' 에러 (0) 2023.03.15 [WebFlux] publisher(발행) - subscriber(구독) 패턴에 대해서 (0) 2020.01.24 [WebFlux] StepVerifier and How to use it 해답 코드 - techio (0) 2019.10.29 [WebFlux] Learn how to create Mono instances 해답 코드 - techio (0) 2019.10.22 [WebFlux] Learn how to create Flux instances 해답 코드 - techio (0) 2019.10.21