본문 바로가기

Spring/기타

undertow upload max-file-size exception handler

반응형

아래와 같이 spring 의 upload file size를 설정을 하면 파일 업로드시 최대 파일 사이즈가 제한되고 

500에러가 발생하게 되는데.  보통 서버에서 500에러는 알수 없는 에러 의도치 않은 런타임 에러를 생각하게 되고 

프론트에서도 500에러는 서버 장애 또는 알수 없는 에러로 취급하고 있다. 

그래서 500에러가 아닌 400 에러의 custom exception 을 리턴하고 에러메세지도 상황에 맞게 수정하고 싶은 경우에 어떻게 하면 좋을지에 대해 알아 보자. 

 

springboot의 was는 보통 tomcat , netty , undertow 이렇게 사용하고 , tomcat이나 netty는 file size exception handling을 같은방식으로 개발하면 되는데 

springboot was서버로 undertow를 사용할 경우  아래와 같은 설정을 적용하고 exception을 핸들링 하려고  일반적인 

@ControllerAdvice를 사용하면 exception 핸들링이 되지 않는다. 

 

아래는 tomcat을 사용했을때 max-file-size exception handling. 방법이다.

@ControllerAdvice
public class GeneralExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public ResponseEntity<ErrorResponse> handleMaxSizeException(MaxUploadSizeExceededException ex) {
        long maxSizeInMB = ex.getMaxUploadSize() / 1024 / 1024;
        ErrorResponse err = ErrorResponse.builder().message("Maximum upload size of " + maxSizeInMB + " MB exceeded").build();
        return new ResponseEntity<>(err, HttpStatus.PAYLOAD_TOO_LARGE);
    }
}

tomcat이나 netty는 위와 같이 500 에러 에서 413 에러로 변경을 하는 방법이 있는데 . 

 

undertow는 위의 방법으로 에러가 catch되지 않는다.  그럼 어떻게 해야 할까 찾아 보았지만 . 우리가 할수 있는게 없다는 

stackoverflow의 개발자 친구들의 답변을 받았고, 그래도 방법을 찾아 보고 싶어 아래와 같은 방법으로 처리가 가능했다.

 

바로 filter에서 처리하는 방법이다. 필터에 try catch를 걸어서 exception이 발생하면 해당 exception이 file size 에러인지 판별해서 에러 메세지를 변경하는 방법이다.

 

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    if(urlPatternMatcher.matches(request)){
        try {
            filterChain.doFilter(request, response);
        } catch (NestedServletException | IllegalStateException | RequestTooBigException | MultiPartParserDefinition.FileTooLargeException ex) {

            if (ex instanceof RequestTooBigException
                    || ex instanceof MultiPartParserDefinition.FileTooLargeException) {
                writeResponse(request, response);
                return;
            }

            Throwable causeException = ex.getCause();
            if (nonNull(causeException)
                    && (causeException instanceof RequestTooBigException
                    || causeException instanceof MultiPartParserDefinition.FileTooLargeException)) {
                writeResponse(request, response);
                return;
            }

            if (ex.getMessage().contains("FileTooLargeException") || ex.getMessage().contains("RequestTooBigException")) {
                Throwable causeException2 = causeException.getCause();
                if (nonNull(causeException2)
                        && (causeException2 instanceof RequestTooBigException
                        || causeException2 instanceof MultiPartParserDefinition.FileTooLargeException)) {
                    writeResponse(request, response);
                    return;
                }
            }
            throw ex;
        }
    }else{
        response.sendError(HttpStatus.NOT_FOUND.value());
    }
}

private void writeResponse(HttpServletRequest request, HttpServletResponse response) throws IOException {
    response.setStatus(HttpStatus.BAD_REQUEST.value());
    response.setCharacterEncoding("UTF-8");
    response.setHeader("Content-Type", "application/json;charset=UTF-8");

    Map<String, Object> attributes = new LinkedHashMap<>();
    attributes.put("timestamp", LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
    attributes.put("path", request.getRequestURI());
    attributes.put("code", String.format("%d.00000", HttpStatus.BAD_REQUEST.value()));
    attributes.put("type", "BAD_REQUEST");
    attributes.put("message", "파일이 사이즈가 너무 큽니다. 20MB까지만 업로드 가능합니다.");
    attributes.put("fieldErrors", null);
    attributes.put("data", null);
    attributes.put("statusMessage", "파일이 사이즈가 너무 큽니다. 20MB까지만 업로드 가능합니다.");
    attributes.put("statusCode", String.format("%d.00000", HttpStatus.BAD_REQUEST.value()));
    response.getWriter().write(new ObjectMapper().writeValueAsString(attributes));
}

위와 같이 하면 exception에 대한 처리가 가능했지만 .. 아름다운 모습은 아닌거 같다. 하지만 현재로써는 undertow was를 사용할 경우 최선의 방법인것 같다.

'Spring > 기타' 카테고리의 다른 글

springboot custom Pageable default annotation  (0) 2022.12.06