Spring WebClient text/html 응답 처리하기
Resolve text/html Response with Spring WebClient
국가 기관에서 제공하는 공공 API를 확인하던 중 응답 페이로드는 JSON 형식인데 응답 헤더의 Content-Type이 text/html
로 전송되어 Spring WebClient에서 UnsupportedMediaTypeException
예외가 발생하였습니다.
이렇게 설계된 것 자체가 특이한 상황이기 때문에 해당 서버의 API를 수정하는 것이 정상적인 처리 과정이겠지만 공공 API라서 어떻게 할 수 없는 상황입니다. 다행히 Stack Overflow에서 동일한 이슈를 발견하여 어렵지 않게 해결할 수 있었습니다.
Issue
해당 이슈는 WebClient에서 학교 기본 정보 NEIS Open API를 호출하고,
SchoolInfoApiPayload payload = createWebClient()
.get()
.uri(uriBuilder -> uriBuilder
.path("/hub/schoolInfo")
.queryParam("Type", "json")
.queryParam("SCHUL_NM", "서울과학고등학교")
.build())
.retrieve()
.bodyToMono(SchoolInfoApiPayload.class)
.block();
응답 페이로드를 그 스펙에 대응하는 객체 타입으로 바인딩 하려고 했을 때 발생하였습니다.
Content type 'text/html;charset=UTF-8' not supported for bodyType=dev.catsriding.openapi.WebClientTest$SchoolInfoApiPayload
org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'text/html;charset=UTF-8' not supported for bodyType=dev.catsriding.openapi.WebClientTest$SchoolInfoApiPayload
학교 기본 정보 NEIS Open API 스펙은 다음 HTTP 요청을 통해 확인할 수 있습니다.
GET /hub/schoolInfo?Type=json&SCHUL_NM=서울과학고등학교 HTTP/1.1
Content-Type: application/json
Cookie: WMONID=MqN60S8Elb0
Host: open.neis.go.kr
Connection: close
User-Agent: RapidAPI/4.2.0 (Macintosh; OS X/14.1.2) GCDHTTPRequest
Resolve
Stack Overflow에서 제안한 방법과 이런저런 시도 끝에 세 가지 방안이 마련되었습니다.
exchangeStrategies()
를 활용하여text/html
형식도 디코딩 할 수 있도록 Jackson Codec을 설정하는 방법- 응답 헤더 Content-Type 값을
application/json
으로 바꿔치기하는 방법 - String 문자열에 응답 페이로드를 담은 다음 JSON으로 역직렬화 하는 방법
ExchangeStrategies
WebClient.builder()
팩토리 메서드를 통해 WebClient 인스턴스를 생성할 때, exchangeStrategies()
에 text/html
형식도 디코딩 할 수 있도록 Jackson Codec을 구성합니다.
private WebClient createWebClient() {
return WebClient.builder()
.baseUrl("https://open.neis.go.kr")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.exchangeStrategies(ExchangeStrategies.builder().codecs(this::acceptedCodecs).build())
.build();
}
private void acceptedCodecs(ClientCodecConfigurer configurer) {
configurer.customCodecs().registerWithDefaultConfig(new Jackson2JsonDecoder(new ObjectMapper(), TEXT_HTML));
configurer.customCodecs().registerWithDefaultConfig(new Jackson2JsonEncoder(new ObjectMapper(), TEXT_HTML));
}
Codec을 추가한 WebClient 인스턴스로 다시 API를 요청해 보면 예외 없이 정상적으로 응답 페이로드가 객체에 바인딩 되었습니다.
Deserialize JSON
이번에는 응답 페이로드에 담긴 JSON 데이터를 String
문자열로 받은 다음 ObjectMapper
를 통해 JSON으로 역직렬화하는 방법입니다.
String response = createWebClient()
...
.bodyToMono(String.class)
.block();
ObjectMapper objectMapper = new ObjectMapper();
SchoolInfoApiPayload payload = objectMapper.readValue(response, SchoolInfoApiPayload.class);
요청 처리 결과 동일한 결과를 얻을 수 있었습니다.
- Spring
- Reactive