spring boot 에서 특정 path의 Request 를 다른 서버로 전달하자. 리버스 프록시
A서버로 들어오는 특정 path하위의 모든 Request를 B서버로 전달하려고 한다.
Spring cloud ZUUL을 사용해도 되지만. RestTemplate를 사용하여 호출해보려 한다.
내 경우엔 client 의 모든 Request HttpMethod를 POST로 변경하고
원래 요청하는 HttpMethod를 “ x-origin-method”라는 커스텀 헤더로 담아서 서버로 요청했다.
@PostMapping("/proxy/**")
public ResponseEntity<byte[]> proxy(HttpServletRequest request, HttpServletResponse response, @RequestBody(required = false) byte[] body) throws IOException, URISyntaxException {
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpRequestFactory.setConnectionRequestTimeout(360000); //6m
httpRequestFactory.setConnectTimeout(360000); //6m
httpRequestFactory.setReadTimeout(360000); //6m
// restTempate tobe bean
RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
// url
String originReqURL = request.getRequestURI().replaceAll("^/proxy", "");
String originQueryString = request.getQueryString();
String urlStr = [대상서버] + originReqURL + (StringUtils.isEmpty(originQueryString) ? "" : "?"+originQueryString);
URI url = new URI(urlStr);
// method
String originMethod = request.getHeader("x-origin-method");
HttpMethod method = HttpMethod.valueOf(originMethod.toUpperCase());
// header
Enumeration<String> headerNames = request.getHeaderNames();
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
while(headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
headers.add(headerName, headerValue);
}
// http entity (body, header)
HttpEntity<byte[]> httpEntity = new HttpEntity<>(body, headers);
return restTemplate.exchange(url, method, httpEntity, byte[].class);
}
대상 서버로 요청되는 url 과 queryString을 조합하고
넘겨야 할 헤더들 또한 전부 포함시킨다.
Body전달을 위해서 @RequestBody 의 타입을 byte[] 로 지정했다.
ByteArrayMessageConverter 외에 다른 MessageConverter는 거치지 않고
HTTP Body가 byte[] body 로 바인딩된다.
요청에 대한 커넥션 제어는 RestTemplate의 HttpClient를 제어하면 되겠다.
multipart/form-data 요청에서 @RequestBody byte[] body의 값이 null 로 넘어오는 현상이 발생하였다.
CommonsMultipartResolver를 Bean 등록하여 Request parsing 시점에 확인해보니 이 시점까지는 request의 inputStream에 정상적으로 body 데이터가 존재하였다.
하지만 MultipartResolver를 거치며 multipartParam 데이터로 파싱되며 RequestBody 에서는 읽을 수 없는 것으로 보였다.
등록한 CommonsMultipartResolver Bean을 제거해 보아도 동일 현상 발생.
원인은
SpringBoot 에서는 기본적으로 MultipartAutoConfiguration을 통해 MultipartResolver를 등록하고 있다.
해결은
AutoConfiguration 대상에서 제외처리 하는 것이다.
@SpringBootApplication(exclude = {MultipartAutoConfiguration.class})
적용 후 확인 시
MultipartResolver 를 통해 multipart 파싱 처리 없이 request 에서 raw body를 읽을 수 있었다.
끄읕