[DevOps] Failed to parse multipart servlet request
개발팀에서 지원요청이 왔다.
문제의 로직은 타 인터페이스 시스템에서 우리 시스템으로 파일업로드할 때, 컨트롤러 단에서 URI 중 특정 path를 @RequestMapping 어노테이션으로 정의하여 받아내는 부분이다.
현상은 로컬서버에서는 이상없는데 개발서버에서는 @RequestMapping 어노테이션으로 정의한 변수에 원하는 값과 다른 값으로 매핑된다는 것!
이 이슈는 개발팀에서 해결하였지만 나에게도 얻어 가는 부분은 꽤나 있었다. 데이터 전송, HTTP 헤더, 개발적인 요소 등등…
개발 환경
로컬서버는 tomcat 9버전을 사용하며, 개발/운영서버는 쿠버네티스 환경에서 jeus 8버전 서버를 사용하고 있다.
- Spring 5.3.19 / Spring Boot 2.6.7
- Sevlet 3.1 (개발서버) / Servlet 4.0 (로컬서버)
- Jeus 8에서는 Servlet 3.1을 지원하고 Tomcat 9에서는 Servlet 4.0을 지원해서 Servlet 3.1을 사용하려면 Tomcat 8버전대를 사용해야 한다
문제 현상
타 인터페이스 시스템에서 우리 시스템으로 파일을 업로드하기 위해 /a/b/c
path로 content-type은 multipart/form-data
로 해서 GET
요청을 보냈을 때, getRequestURI();
로 요청 url을 확인해 보면 /a/b/c
path 대로 들어온 것은 확인 가능했다.
그러나 @RequestMapping 어노테이션으로 /a/{requestMapping}/**
값을 매핑시켰으나 requestMapping에는 엉뚱한 a가 입력됐다.
원인
파일을 전송하게 될 때는 바이너리 형태로 request를 던지게 된다. 바이너리 형태로 받으려면 요청 헤더의 content-type이 multipart/form-data
여야 한다.
tomcat에서는 allowCasualMultiParsing=”true”
로 설정하면 데이터를 파싱할 수 있는데
jeus에서는 해당 설정이 없어서 서버단에서 받아내지 못하니 데이터 파싱이 안됐다.
Failed to parse multipart servlet request; nested exception is javax.servlet.ServletException: The request's content-type is not multipart/form-data.
그래서 servlet 단에서 받아 내야 하는데 이상하게도 애플리케이션 설정에서 spring.servlet.multipart.enabled
의 defualt 값이 true임에도 불구하고 jeus 서버는 데이터를 처리하지 못했다.
++) jeus에서 제공하는 servlet 라이브러리를 확인해 보니 Multipart.class에서 POST 메소드가 아니면 false로 반환하게끔 하드코딩되어 있었다!
public boolean isMultipart() {
if (!this.requestImpl.getMethod().toLowerCase().equals("post"))
return false;
String contentType = this.requestImpl.getContentType();
return (contentType != null && contentType.toLowerCase(Locale.ENGLISH).startsWith("multipart/form-data"));
}
해결 방법
- 요청 메소드를 GET에서 POST 방식으로 변경
- Header와 Body를 받아오는 부분 제거 (@RequestHeader, @RequestBody)
결과
바이너리 형태의 데이터 값을 받으려면 Content-type이 multipart/form-data
이어야 서버에서 요청을 제대로 처리할 수 있다.
그럼 요청 헤더의 Content-type을 지정해야 하는데, GET 방식은 URL을 이용하여 요청을 전송하기 때문에 적합하지 않으므로 POST 방식으로 변경하는게 적합하다.
추가로 알아본 내용으로는,
보통 multipart/form-data
로 인코딩된 데이터는 파일 업로드 등 복잡한 형태의 데이터를 전송하는 데 적합한데, 이러한 경우 보통 POST 방식을 사용하는게 맞다고 한다.
POST 방식은 요청 본문에 데이터를 포함하기 때문에 데이터 크기에 대한 제약이 없어, multipart/form-data 형식으로 데이터를 전송하는 데 적합하기 때문이라고 한다!
참고
HttpServletRequest 함수를 사용한 요청 URL 취득
FileUpload Multipart 설정 (Tomcat)
Leave a comment