Spring Jackson json Parsing error - unquoted chars...

정명진·2022년 12월 23일
0

애플 프로비저닝 발급을 일반 유저들도 할 수 있도록 구현하는 프로젝트를 진행중에 post 요청시 parsing 에러가 발생하는 현상이 생겼다.
처음보는 에러라 당황스러워 감이 잡히지 않았다.

Illegal unquoted character ((CTRL-CHAR, code 28)): has to be escaped using backslash to be included in string value

파싱 자체를 못하는가 싶어 로그를 찍어 확인해보니 값을 잘 받아 온다.
여기서 멘붕이 왔다. 값도 잘 받아오는데 왜 한글만 매핑을 못하지? 영어는 잘 매핑이 되는데...
특히 request의 inputStream을 가져오려고 하면 문제가 발생했다. 그래서 이전에 요청 바디의 내용을 보려고 구현했던 HttpServletRequestWrapper를 제거하고 다시 요청하니 성공적으로 동작했다. 아래는 문제가 발생했던 클래스의 코드다. getInputStream()에서 문제가 발생하는것이었다. 원인을 추론해보니 영어는 잘되는걸로 보아 해당 과정에서 인코딩 설정이 되지 않은채 한글을 파싱하다가 문제가 생기지 않을까? 추론을 했다.

package mj.provisioning.logging;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.io.StringReader;
import java.util.Scanner;

@Slf4j
public class RequestServletWrapper extends HttpServletRequestWrapper {
    private String requestData = null;
    /**
     * Constructs a request object wrapping the given request.
     *
     * @param request The request to wrap
     * @throws IllegalArgumentException if the request is null
     */
    public RequestServletWrapper(HttpServletRequest request) {
        super(request);

        try(Scanner s = new Scanner(request.getInputStream()).useDelimiter("\\A")){
            requestData = s.hasNext() ? s.next() : "";
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        StringReader stringReader = new StringReader(requestData);
        return new ServletInputStream() {
            private ReadListener readListener = null;

            @Override
            public boolean isFinished() {
                try{
                    return stringReader.read()<0;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return false;
            }

            @Override
            public boolean isReady() {
                return isFinished();
            }

            @Override
            public void setReadListener(ReadListener listener) {
                this.readListener = listener;

                try{
                    if (!isFinished()){
                        readListener.onDataAvailable();
                    }else {
                        readListener.onAllDataRead();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

            @Override
            public int read() throws IOException {
                return stringReader.read();
            }
        };
    }
}

찾아보니 StringReader는 encoding을 줄 수 있는곳이 없길래 다른 걸로 구현해서 해결했다.
코드는 다음과 같이 변경했다.

@Override
    public ServletInputStream getInputStream() throws IOException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestData.getBytes(StandardCharsets.UTF_8));
        return new ServletInputStream() {
            private ReadListener readListener = null;

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener listener) {
            }

            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };

파싱도 정상적으로 성공하여 아래와 같이 출력이 된다.

RequestBody {name=test_with_readline~~~~~~~}

프로비저닝 자동화 곧 있으면 끝이다 야호~

profile
개발자로 입사했지만 정체성을 잃어가는중... 다시 준비 시작이다..

0개의 댓글