Get XML from HttpServletRequest and use into endpoint

走远了吗. 提交于 2019-11-29 13:09:11

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.

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;
    }

}

You dont need to do anything special here, Spring framework will do it for you. All you need is:

  1. Create a Pojo or Bean which represents your XML data.

  2. Add xml data format dependency to Gradle/Maven which will bind the request xml to your pojo.

     compile group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-xml', version: '2.9.9'
    
  3. Tell your request handler to accept XML like this:

    @RequestMapping(value = "/xmlexample", method = RequestMethod.POST,consumes = "application/xml;charset=UTF-8") 
    public final boolean transactionHandler(@Valid @RequestBody Transaction transaction) {
        log.debug("Received transaction request with data {}", transaction);
        return true;
    }
    

And voila, you will have your transaction bean populated with your XML data.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!