[Spring]JsonParseException 해결기

minn·2023년 6월 26일
0

요즘 진행하고 있는 프로젝트에서, S3에 파일을 업로드하는 간단한 API를 만들었는데 이게 JsonParseException이 발생하면서 제대로 동작하지 않았다. 에러 내용과 함께 디버그 과정을 기록하려고 한다.

form-data 형식으로 file을 받아서 S3 업로드를 하는 기능을 만들었다. 소스 코드는 다음과 같다.

S3Controller.java

@Slf4j
@Controller
@RequestMapping("/s3")
@RequiredArgsConstructor
@Api("S3 파일 업로드")
public class S3UploadController {
    private final AmazonS3Client amazonS3Client;

    @Value("${cloud.aws.s3.bucket}")
    private String bucket;
    @Value("${cloud.aws.region.static}")
    private String region;

    @PostMapping("/upload")
    @ApiOperation(value = "S3에 파일을 업로드한다")
    @ResponseBody
    public ResponseVO<List<FileDTO>> uploadFile(@RequestParam("file") MultipartFile file) {
        try {
            String fileName=file.getOriginalFilename();
            String fileUrl= "https://" + bucket + ".s3." + region + ".amazonaws.com/" + fileName;

            ObjectMetadata metadata= new ObjectMetadata();
            metadata.setContentType(file.getContentType());
            metadata.setContentLength(file.getSize());
            amazonS3Client.putObject(bucket, fileName, file.getInputStream(), metadata);

            return new ResponseVO<>(ResponseCode.C,"저장 완료", null);

        } catch (IOException e) {
            e.printStackTrace();
            return new ResponseVO<>(ResponseCode.C,"IOException",null);
        }
    }
}

이렇게 코딩을 한 후 Postman에서 테스트를 해보면 500 에러가 떴다.

  • 에러 코드 : 500 Internal Server Error
  • 에러 메시지 : Request method 'POST' not supported

그리고 에러 로그는 이렇게 떴다.

(생략)
.
.
.
ERROR 2023-06-20 22:44:51[http-nio-8080-exec-8] org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet][log:175] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
com.fasterxml.jackson.core.JsonParseException: Unexpected character ('-' (code 45)) in numeric value: expected digit (0-9) to follow minus sign, for valid numeric value
 at [Source: (StringReader); line: 1, column: 3]
	at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2391)
	at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:735)
	at com.fasterxml.jackson.core.base.ParserMinimalBase.reportUnexpectedNumberChar(ParserMinimalBase.java:557)
	at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._handleInvalidNumberStart(ReaderBasedJsonParser.java:1718)
	at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._parseNegNumber(ReaderBasedJsonParser.java:1467)
	at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:784)
	at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4762)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4668)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3630)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3598)
	at com.whale.partyplace.filter.RequestWrapper.readJSONStringFromBody(RequestWrapper.java:102)
	at com.whale.partyplace.filter.RequestWrapper.<init>(RequestWrapper.java:36)
	at com.whale.partyplace.filter.XSSFilter.doFilterInternal(XSSFilter.java:30)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
.
.
.
(생략)

com.fasterxml.jackson.core.JsonParseException: Unexpected character ('-' (code 45)) in numeric value: expected digit (0-9) to follow minus sign, for valid numeric value
at [Source: (StringReader); line: 1, column: 3]

나는 MultipartFile을 요청값으로 보내고 있는데 JsonParse 예외가 발생한다. 뭔가 잘못됐다.

실무로 일하고 계신 선배님의 도움을 받아 디버깅을 해 본 결과 요청 객체가 controller에 들어오지도 않는다는 걸 알았다. controller에 들어오기 전에 어딘가에서 처리되고, 그 처리하는 과정에서 json Parse 문제가 발생한다는 것이었다.

계속해서 디버깅을 해 본 결과 문제는 에러로그에도 찍혀있듯이 XSSFiler였다.

public class XSSFilter extends OncePerRequestFilter {
...
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        final HttpServletRequest requestWrapper = request.getRequestURI().startsWith("/file/")?request:new RequestWrapper(request);
        final ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
        chain.doFilter(requestWrapper, responseWrapper);
    }
    ...
}

XSS 해킹에 대한 보안을 위해 모든 요청과 응답은 이 XSSFilter 클래스의 requestWrapper와 responseWrapper를 거쳐서 처리되는데,
request uri에 /file/이 포함되어 있으면 이 Filter를 거치지 않게 된다.

해당 XSSFilter 클래스를 내가 작성한 게 아니라서 이 부분을 모르고 있었다... 그래서 이 filter를 거치지 않게 하기 위해 uri에 /file/ 이라는 경로를 추가해주었다.

S3에 정상적으로 업로드 되는 것을 확인했다!

결론 : json으로 보내지 않는데 jsonParse 에러가 뜬다면, XSSFilter에 걸린 건지 확인해봐라.

profile
Backend Developer

0개의 댓글