java - 从 HttpServletRequest 获取 XML 并用于端点

标签 java spring rest spring-boot spring-rest

我想从请求和响应中获取 XML 数据并将其用于 Rest Controller 。我试过这个:

@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:流已关闭

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

编辑:我也尝试过使用过滤器的解决方案,但我不知道如何将请求负载使用到休息 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
     } 
}

如何将 HttpLoggingFilter 调用到 Java 方法 handleMessage 中并以正文字符串形式获取请求?也许我可以让它服务并注入(inject)它?您能给我一些如何评估代码的建议吗?

最佳答案

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

在前两行中,我将请求和响应包装在 requestToUse 和 responseToUse 中。 ResetableStreamHttpServletRequest 和 ResettableStreamHttpServletResponse 是包装类,它们将原始字符串主体保留在其中,每次需要流时,它们都会返回一个新流。然后,您就忘记了请求和响应,并开始使用 requestToUse 和 responseToUse。

我从我做的一个旧项目中得到了这个。其实还有更多的类,我把主要部分摘出来给大家。这可能不会立即编译。尝试一下并告诉我,我会帮助您实现它。

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

}

关于java - 从 HttpServletRequest 获取 XML 并用于端点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56265334/

相关文章:

java.lang.NoSuchFieldError : INSTANCE

json - Elasticsearch 删除映射属性

java - Spring MVC : Is it possible to bind a specific HttpMessageConverter to a @RequestBody-annotated parameter?

java - 使用 JDBC 在 Postgres 中使用串联列运行选择时出错

java - 如何将类的一个参数的值复制到另一个Collection对象?

html - Bootstrap col-md 剪切标签的文字

spring - java.lang.IllegalArgumentException : Can not set java. lang.Short 字段到 java.lang.Short

python - 解析 json 数据时仅检索前 2 个值

java - 从 android 发出 JSON 请求

java - 无法停止工作线程,可能会造成内存泄漏。