Java spring获取post请求的主体

时间:2014-10-15 10:25:29

标签: spring post interceptor

我有以下问题:我在弹簧控制器处理之前尝试获取POST请求的主体。为此我使用HandlerInterceptorAdapter的preHandle()方法。

如本讨论中所述Spring REST service: retrieving JSON from Request我也使用HttpServletRequestWrapper。使用此包装器,我设法打印第一个POST请求的主体,但第二个POST抛出IOException:StreamClosed。

您对我如何获取所有POST请求的主体有什么想法吗?

这是来自拦截器的preHandle()方法:

@Override
public boolean preHandle(HttpServletRequest request,
        HttpServletResponse response, Object handler) throws Exception {
    System.out.println(request.getMethod());
    MyRequestWrapper w = new MyRequestWrapper(request);

    BufferedReader r = w.getReader();
    System.out.println(r.readLine());

    return super.preHandle(request, response, handler);
}

HttpServletRequestWrapper:

public class MyRequestWrapper extends HttpServletRequestWrapper {
private ByteArrayOutputStream cachedBytes;
private HttpServletRequest request;

public MyRequestWrapper(HttpServletRequest request) {
    super(request);
    this.request = request;
}

@Override
public ServletInputStream getInputStream() throws IOException {
    cachedBytes = new ByteArrayOutputStream();

    if (request.getMethod().equals("POST"))
        cacheInputStream();

    return new CachedServletInputStream();
}

@Override
public BufferedReader getReader() throws IOException {
    return new BufferedReader(new InputStreamReader(getInputStream()));
}

private void cacheInputStream() throws IOException {
    /*
     * Cache the inputstream in order to read it multiple times. For
     * convenience, I use apache.commons IOUtils
     */
    ServletInputStream inputStream = super.getInputStream();

    if (inputStream == null) {
        return;
    }

    IOUtils.copy(inputStream, cachedBytes);
}

/* An inputstream which reads the cached request body */
public class CachedServletInputStream extends ServletInputStream {
    private ByteArrayInputStream input;

    public CachedServletInputStream() {
        /* create a new input stream from the cached request body */
        input = new ByteArrayInputStream(cachedBytes.toByteArray());
    }

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

控制台输出:

2014-10-15 12:13:00 INFO [http-nio-8080-exec-1] org.springframework.web.servlet.DispatcherServlet - FrameworkServlet 'dispatcherServlet': initialization completed in 9 ms
GET
null
GET
null
POST
{"long":null,"owner":{"__type":"Owner","id":20,"version":1,"md5Password":""},"string":"ws","tool":{"__type":"Tool","id":33,"version":1}}
POST
2014-10-15 12:13:00 ERROR [http-nio-8080-exec-3] org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet] - Servlet.service() for servlet dispatcherServlet threw exception
java.io.IOException: Stream closed

2 个答案:

答案 0 :(得分:1)

您尝试从Wrapper中的原始请求中读取,但在此之后,原始请求仍在被读取 - 因此请求输入流已被使用且无法再次读取。

不要使用Interceptor,而应考虑使用javax.servlet.Filter。在doFilter方法中,您可以在链上传递包装的请求。

答案 1 :(得分:0)

我使用过滤器来实现Filter&扩展HandlerInterceptorAdapter的拦截器(因为在过滤器中所有字段都可以为空,我不能将任何内容保存到DB。请参阅Autowired Null Pointer Exception)来检索请求和响应正文并将它们保存到DB。如果您的过滤器工作正常,则只使用过滤器。

滤波器。在这里,我包装一个请求和一个响应,不仅要读取一次。您可以使用ContentCachingRequestWrapper和ContentCachingResponseWrapper。

    @Component
    public class RequestLogFilter implements Filter {

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

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub

    }

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

        logger.info("======================> FILTER <======================");

        HttpServletRequest requestToCache = new ContentCachingRequestWrapper((HttpServletRequest) request);
        HttpServletResponse responseToCache = new ContentCachingResponseWrapper((HttpServletResponse) response);

        // before method
        chain.doFilter(requestToCache, responseToCache);
        // after method

        // your logic(save to DB, logging...)
        getRequestData(request);
        getResponseData(response);
    }

    @Override
    public void destroy() {

    }

}

-

@Component
public class RequestLogInterceptor extends HandlerInterceptorAdapter {

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

    @Autowired
    private InboundRequestLogStore inboundRequestLogStore;

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {

        logger.info("====================> INTERCEPTOR <========================");

        try {
            if (request.getAttribute(InboundRequestAspect.INBOUND_LOG_MARKER) != null) {

                InboundRequestLogRecord logRecord = new InboundRequestLogRecord();

                logRecord.setIpAddress(request.getRemoteAddr());

                // getting request and response body
                logRecord.setRequestBody(getRequestData(request));
                logRecord.setResponseBody(getResponseData(response));

                logRecord.setResponseCode(((HttpServletResponse) response).getStatus());

                String uri = request.getScheme() + "://" + request.getServerName()
                        + ("http".equals(request.getScheme()) && request.getServerPort() == 80
                                || "https".equals(request.getScheme()) && request.getServerPort() == 443 ? ""
                                        : ":" + request.getServerPort())
                        + request.getRequestURI()
                        + (request.getQueryString() != null ? "?" + request.getQueryString() : "");
                logRecord.setUrl(uri);
                inboundRequestLogStore.add(logRecord);  // save to DB

              } else {

                        ((ContentCachingResponseWrapper) response).copyBodyToResponse(); // in other case you send null to the response

              }


        } catch (Exception e) {
            logger.error("error ", e);

            try {
                    ((ContentCachingResponseWrapper) response).copyBodyToResponse(); // in other case you send null to the response
                } catch (Exception e2) {
                    // TODO Auto-generated catch block
                    logger.error("error ", e2);
                }

        }



    }

    public static String getRequestData(final HttpServletRequest request) throws UnsupportedEncodingException {
        String payload = null;
        ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
        if (wrapper != null) {
            byte[] buf = wrapper.getContentAsByteArray();
            if (buf.length > 0) {
                payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
            }
        }
        return payload;
    }

        public static String getResponseData(final HttpServletResponse response) throws UnsupportedEncodingException, IOException {
            String payload = null;
            ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
            if (wrapper != null) {
                byte[] buf = wrapper.getContentAsByteArray();
                if (buf.length > 0) {
                    payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
                }
                wrapper.copyBodyToResponse();   // in other case you send null to the response
            }
            return payload;
        }

    }

添加到servlet-context.xml

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <beans:bean class="path.to.RequestLogInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

名称空间:

<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc 
    http://www.springframework.org/schema/mvc/spring-mvc.xsd">

ContentCachingRequestWrapper - http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/util/ContentCachingRequestWrapper.html

ContentCachingResponseWrapper - http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/util/ContentCachingResponseWrapper.html