복붙노트

[SPRING] POST 요청에서 XML을 가져 와서 서블릿 필터에서 수정하는 방법?

SPRING

POST 요청에서 XML을 가져 와서 서블릿 필터에서 수정하는 방법?

현재 요청이 스프링 컨트롤러에 도달하기 전에 서블릿 필터에서 XML (POST 요청으로부터)을 가져와야하는 요구 사항을 처리 중입니다. 그런 다음 XML에서 (빈 노드 / 요소를 잘라내서) XML을 처리해야합니다. 필터를 누른 다음 통화가 더 진행되어야합니다.

아래 코드 (첨부 된 코드 단편)를 시도하고 요청 본문 (XML)을 가져올 수 있었으며 수정 된 응답을 설정할 수있었습니다.

    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;
    if (httpRequest.getMethod().equalsIgnoreCase("POST")) {
      extractDataFromRequest(httpRequest);
      httpResponse.getWriter().write("<root><root>");
    }
    chain.doFilter(request, wrappedResponse);

   public static String extractDataFromRequest(HttpServletRequest request) throws IOException {

    String line;
    StringBuilder builder = new StringBuilder();
    BufferedReader reader = request.getReader();
    while ((line = reader.readLine()) != null) {
      builder.append(line);
    }
    return builder.toString();
  }

그러나 스프링은 다음 예외를 제외하고 실패했습니다.

Severe: java.lang.IllegalStateException: PWC3997: getReader() has already been called for this request
    at org.apache.catalina.connector.Request.getInputStream(Request.java:1178)
    at org.apache.catalina.connector.RequestFacade.getInputStream(RequestFacade.java:407)
    at org.springframework.http.server.ServletServerHttpRequest.getBody(ServletServerHttpRequest.java:165)
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:120)
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:100)

나는이 요구 사항에 대한 구체적인 구현을 전문가로부터 찾고있다.

해결법

  1. ==============================

    1.InputStream을 두 번 사용할 수 없으므로 반복적 인 InputStream 복사본을 유지하는 래퍼 클래스를 만들어야합니다.

    InputStream을 두 번 사용할 수 없으므로 반복적 인 InputStream 복사본을 유지하는 래퍼 클래스를 만들어야합니다.

    public class ReadTwiceHttpServletRequestWrapper extends HttpServletRequestWrapper {
    
    private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    
    public ReadTwiceHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        try {
            IOUtils.copy(request.getInputStream(), outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(outputStream.toByteArray())));
    }
    
    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
        return new ServletInputStream() {
    
            @Override
            public int readLine(byte[] b, int off, int len) throws IOException {
                return inputStream.read(b, off, len);
            }
    
            @Override
            public boolean isFinished() {
                return inputStream.available() > 0;
            }
    
            @Override
            public boolean isReady() {
                return true;
            }
    
            @Override
            public void setReadListener(ReadListener arg0) {
                // TODO Auto-generated method stub
            }
    
            @Override
            public int read() throws IOException {
                return inputStream.read();
            }
        };
    }
    
    public void setBody(String body) {
        outputStream = new ByteArrayOutputStream();
        try {
            outputStream.write(body.getBytes());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    public String getBody() {
        return new String(outputStream.toByteArray());
    }
    

    }

    그런 다음 체인의 첫 번째 필터를 사용하여이를 초기화해야합니다.

    public class ReadTwiceFilter implements Filter {
    
    @Override
    public void destroy() {
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
    
        ReadTwiceHttpServletRequestWrapper readTwiceHttpServletRequestWrapper = new ReadTwiceHttpServletRequestWrapper(
                (HttpServletRequest) request);
    
        String newBody = readTwiceHttpServletRequestWrapper.getBody().replace("<soap:studentId>1</soap:studentId>", "<soap:studentId>2</soap:studentId>");
        readTwiceHttpServletRequestWrapper.setBody(newBody);
    
        chain.doFilter(readTwiceHttpServletRequestWrapper, response);
    }
    
    @Override
    public void init(FilterConfig arg0) throws ServletException {
    }
    

    }

  2. ==============================

    2.getReader 메소드 대신에 getInputStream를 사용하도록 필터에서 구현을 변경하십시오. 이 문제는 전체 구현이 ServletRequest에서 getReader 및 getInputStream 메소드를 호출 할 때 발생합니다. javadoc에서 언급했듯이 그 중 하나만 호출 할 수 있습니다. stacktrace를보고; 컨트롤러 (spring mvc)가 getInputStream을 호출하므로 getReader () 메시지가 이미 호출되었습니다.

    getReader 메소드 대신에 getInputStream를 사용하도록 필터에서 구현을 변경하십시오. 이 문제는 전체 구현이 ServletRequest에서 getReader 및 getInputStream 메소드를 호출 할 때 발생합니다. javadoc에서 언급했듯이 그 중 하나만 호출 할 수 있습니다. stacktrace를보고; 컨트롤러 (spring mvc)가 getInputStream을 호출하므로 getReader () 메시지가 이미 호출되었습니다.

  3. from https://stackoverflow.com/questions/38614799/how-to-get-the-xml-from-post-request-and-modify-it-in-servlet-filter by cc-by-sa and MIT license