从HttpServletRequest获取XML并将其用于端点

从HttpServletRequest获取XML并将其用于端点

问题描述:

我想从请求和响应中获取XML数据,并将其用于Rest控制器.我试过了:

I would like to get the XML data from request and response and use it into Rest controller. I tried this:

@RestController()
    public class HomeController {

        @PostMapping(value = "/v1")
      public Response handleMessage(@RequestBody Transaction transaction, HttpServletRequest request, HttpServletResponse response) throws Exception {

           HttpServletRequest request, HttpServletResponse response

            System.out.println("!!!!!!! InputStream");
            System.out.println(request.getInputStream());
            System.out.println(response.getOutputStream());

            InputStream in = request.getInputStream();
            String readLine;
            BufferedReader br = new BufferedReader(new InputStreamReader(in));

            while (((readLine = br.readLine()) != null)) {
                System.out.println(readLine);    
            }
         } 
    }

但是我得到了java.io.IOException: UT010029: Stream is closed

将内容放入String变量的正确方法是什么?

What is the proper way to get the content into String variable?

编辑:我也尝试了使用Filter的解决方案,但我不知道如何将请求有效负载用于rest控制器:

EDIT: I also tried solution with Filter but I'm not aware how to use the request payload into rest controller:

读取请求有效载荷:

@Component
public class HttpLoggingFilter implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(HttpLoggingFilter.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        ResettableStreamHttpServletRequest wrappedRequest = new ResettableStreamHttpServletRequest((HttpServletRequest) request);
        wrappedRequest.getInputStream().read();
        String body = IOUtils.toString(wrappedRequest.getReader());
        System.out.println("!!!!!!!!!!!!!!!!!! " + body);
        wrappedRequest.resetInputStream();


        chain.doFilter(request, response);
    }

    public class ResettableStreamHttpServletRequest extends HttpServletRequestWrapper {
        private byte[] rawData;
        private HttpServletRequest request;
        private ResettableServletInputStream servletStream;

        ResettableStreamHttpServletRequest(HttpServletRequest request) {
            super(request);
            this.request = request;
            this.servletStream = new ResettableServletInputStream();
        }

        void resetInputStream() {
            servletStream.stream = new ByteArrayInputStream(rawData);
        }

        @Override
        public ServletInputStream getInputStream() throws IOException {
            if (rawData == null) {
                rawData = IOUtils.toByteArray(this.request.getInputStream());
                servletStream.stream = new ByteArrayInputStream(rawData);
            }
            return servletStream;
        }

        @Override
        public BufferedReader getReader() throws IOException {
            if (rawData == null) {
                rawData = IOUtils.toByteArray(this.request.getInputStream());
                servletStream.stream = new ByteArrayInputStream(rawData);
            }
            String encoding = getCharacterEncoding();
            if (encoding != null) {
                return new BufferedReader(new InputStreamReader(servletStream, encoding));
            } else {
                return new BufferedReader(new InputStreamReader(servletStream));
            }
        }

        private class ResettableServletInputStream extends ServletInputStream {
            private InputStream stream;

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

            @Override
            public boolean isFinished() {
                // TODO Auto-generated method stub
                return false;
            }

            @Override
            public boolean isReady() {
                // TODO Auto-generated method stub
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
                // TODO Auto-generated method stub

            }
        }
    }   
}

其他端点:

@RestController()
public class HomeController {

    @PostMapping(value = "/v1")
  public Response handleMessage(@RequestBody Transaction transaction, HttpServletRequest request, org.zalando.logbook.HttpRequest requestv, HttpServletResponse response) throws Exception {

       // Get here request and response and log it into DB
     } 
}

如何在Java方法handleMessage中调用HttpLoggingFilter并将请求作为正文String获取?也许我可以提供服务并注入它?您能给我一些建议,以帮助我评估代码吗?

How I can call HttpLoggingFilter into the Java method handleMessage and get the request as body String? Probably I can make it service and Inject it? Can you give me some advice how I can assess the code?

这里有很多类可以做到这一点.这是一次OncePerRequestFilter的实现,请在此处检查 https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/OncePerRequestFilter.html .基本上,问题在于在链式过滤器中,请求流和响应流只能被读取一次.因此,需要将这两个流包装在可以多次读取的内容中.

Here are a bunch of classes to do it. This is a once a OncePerRequestFilter implementation, check here https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/OncePerRequestFilter.html. Basically the problem is that in the chain filter, the request stream and response stream can be read just once. So, need to wrap these 2 streams inside something that can be read more than once.

在前两行中,我将请求和响应包装在requestToUse和responseToUse内. ResettableStreamHttpServletRequest和ResettableStreamHttpServletResponse是包装器类,它们将原始字符串保留在其中,每次需要该流时,它们都将返回一个新流.然后,您就无需再请求和响应了,而是开始使用requestToUse和responseToUse.

In the first 2 lines I wrapped request and response inside requestToUse and responseToUse. ResettableStreamHttpServletRequest and ResettableStreamHttpServletResponse are wrapper classes that keeps the original string body inside of them, and every time the stream is needed they return a new stream.Then from there, you forget about request and response and start using requestToUse and responseToUse.

我是从我做的一个旧项目中摘下来的.其实还有更多的分类,但是我为您提取了主要部分.这可能不会立即编译.尝试一下,让我知道,我将帮助您使其正常工作.

I took this from an old project I did. Actually there are more clases, but I extracted the main parts for you. This may not compile right away. Give it a try and let me know and I will help you to make it work.

    public class RequestResponseLoggingFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //here you wrap the request and response into some resetable istream class
        HttpServletRequest requestToUse = new ResettableStreamHttpServletRequest(request);
        HttpServletResponse responseToUse = new ResettableStreamHttpServletResponse(response);

        //you read the request to log it
        byte[] payload = IOUtils.toByteArray(requestToUse.getReader(), requestToUse.getCharacterEncoding());      
        String body =  new String(payload, requestToUse.getCharacterEncoding());

        //here you log the body request
        log.(body);

        //let the chain continue
        filterChain.doFilter(requestToUse, responseToUse);

        // Here we log the response
        String response =  new String(responseToUse.toString().getBytes(), responseToUse.getCharacterEncoding());

        //since you can read the stream just once, you will need it again for chain to be able to continue, so you reset it
        ResettableStreamHttpServletResponse responseWrapper = WebUtils.getNativeResponse(responseToUse, ResettableStreamHttpServletResponse.class);
        if (responseWrapper != null) {
            responseWrapper.copyBodyToResponse(true);
        }
    }

}

    public class ResettableStreamHttpServletRequest extends HttpServletRequestWrapper {

    private byte[] rawData;
    private ResettableServletInputStream servletStream;

    public ResettableStreamHttpServletRequest(HttpServletRequest request) throws IOException {
        super(request);
        rawData = IOUtils.toByteArray(request.getInputStream());
        servletStream = new ResettableServletInputStream();
        servletStream.setStream(new ByteArrayInputStream(rawData));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        servletStream.setStream(new ByteArrayInputStream(rawData));
        return servletStream;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        servletStream.setStream(new ByteArrayInputStream(rawData));
        return new BufferedReader(new InputStreamReader(servletStream));
    }

}

    public class ResettableStreamHttpServletResponse extends HttpServletResponseWrapper {

    private ByteArrayServletOutputStream byteArrayServletOutputStream = new ByteArrayServletOutputStream();

    public ResettableStreamHttpServletResponse(HttpServletResponse response) throws IOException {
        super(response);
    }

    /**
     * Copy the cached body content to the response.
     *
     * @param complete whether to set a corresponding content length for the complete cached body content
     * @since 4.2
     */
    public void copyBodyToResponse(boolean complete) throws IOException {
        byte[] array = byteArrayServletOutputStream.toByteArray();
        if (array.length > 0) {
            HttpServletResponse rawResponse = (HttpServletResponse) getResponse();
            if (complete && !rawResponse.isCommitted()) {
                rawResponse.setContentLength(array.length);
            }
            rawResponse.getOutputStream().write(byteArrayServletOutputStream.toByteArray());
            if (complete) {
                super.flushBuffer();
            }
        }
    }

    /**
     * The default behavior of this method is to return getOutputStream() on the wrapped response object.
     */
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return byteArrayServletOutputStream;
    }

    /**
     * The default behavior of this method is to return getOutputStream() on the wrapped response object.
     */
    @Override
    public String toString() {
        String response = new String(byteArrayServletOutputStream.toByteArray());
        return response;
    }

}