web-dev-qa-db-ja.com

request.getInputStream()を複数回読み取る方法

私はこのコードを持っています:

_@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    logger.info("Filter start...");

    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;

    String ba = getBaId(getBody(httpRequest));

    if (ba == null) {
        logger.error("Wrong XML");
        httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);
    } else {      

        if (!clients.containsKey(ba)) {
            clients.put(ba, 1);
            logger.info("Client map : init...");
        } else {
            clients.put(ba, clients.get(ba).intValue() + 1);
            logger.info("Threads for " + ba + " = " + clients.get(ba).toString());
        }

        chain.doFilter(request, response);
    }
}
_

このweb.xml(パッケージは短縮され、名前が変更されていますが、見た目は同じです)

_<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app>
  <filter>
    <filter-name>TestFilter</filter-name>
    <filter-class>pkg.TestFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>TestFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>WEB-INF/applicationContext.xml</param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <servlet>
    <servlet-name>Name</servlet-name>
    <display-name>Name</display-name>
    <servlet-class>pkg.Name</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Name</servlet-name>
    <url-pattern>/services/*</url-pattern>
  </servlet-mapping>
</web-app>
_

フィルターの後にサーブレットを呼び出したい。私はchain.doFilter(...)がうまくいくことを望んでいましたが、chain.doFilter(...)の行で常にこのエラーを受け取ります:

_Java.lang.IllegalStateException: getInputStream() can't be called after getReader()
at com.caucho.server.connection.AbstractHttpRequest.getInputStream(AbstractHttpRequest.Java:1933)
at org.Apache.cxf.transport.http.AbstractHTTPDestination.setupMessage(AbstractHTTPDestination.Java:249)
at org.Apache.cxf.transport.servlet.ServletDestination.invoke(ServletDestination.Java:82)
at org.Apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.Java:283)
at org.Apache.cxf.transport.servlet.ServletController.invoke(ServletController.Java:166)
at org.Apache.cxf.transport.servlet.AbstractCXFServlet.invoke(AbstractCXFServlet.Java:174)
at org.Apache.cxf.transport.servlet.AbstractCXFServlet.doPost(AbstractCXFServlet.Java:152)
at javax.servlet.http.HttpServlet.service(HttpServlet.Java:153)
at javax.servlet.http.HttpServlet.service(HttpServlet.Java:91)
at com.caucho.server.dispatch.ServletFilterChain.doFilter(ServletFilterChain.Java:103)
at pkg.TestFilter.doFilter(TestFilter.Java:102)
at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.Java:87)
at com.caucho.server.webapp.WebAppFilterChain.doFilter(WebAppFilterChain.Java:187)
at com.caucho.server.dispatch.ServletInvocation.service(ServletInvocation.Java:265)
at com.caucho.server.http.HttpRequest.handleRequest(HttpRequest.Java:273)
at com.caucho.server.port.TcpConnection.run(TcpConnection.Java:682)
at com.caucho.util.ThreadPool$Item.runTasks(ThreadPool.Java:743)
at com.caucho.util.ThreadPool$Item.run(ThreadPool.Java:662)
at Java.lang.Thread.run(Thread.Java:619)
_
35
user219882

おそらくgetReader()を使用してHttpServletRequestの使用を開始します。

_String ba = getBaId(getBody(httpRequest)); 
_

サーブレットは同じリクエストでgetInputStream()を呼び出そうとしますが、これは許可されていません。行う必要があるのは、 ServletRequestWrapper を使用してリクエストの本文のコピーを作成し、複数のメソッドで読み取ることができるようにすることです。私は完全な例を知っている時間がありません...知っています...申し訳ありません...

8
Guillaume

受け入れられた回答に基づく作業コード。

public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {

private static final Logger logger = Logger.getLogger(CustomHttpServletRequestWrapper.class);
private final String body;

public CustomHttpServletRequestWrapper(HttpServletRequest request) {
    super(request);

    StringBuilder stringBuilder = new StringBuilder();  
    BufferedReader bufferedReader = null;  

    try {  
        InputStream inputStream = request.getInputStream(); 

        if (inputStream != null) {  
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream));  

            char[] charBuffer = new char[128];  
            int bytesRead = -1;  

            while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {  
                stringBuilder.append(charBuffer, 0, bytesRead);  
            }  
        } else {  
            stringBuilder.append("");  
        }  
    } catch (IOException ex) {  
        logger.error("Error reading the request body...");  
    } finally {  
        if (bufferedReader != null) {  
            try {  
                bufferedReader.close();  
            } catch (IOException ex) {  
                logger.error("Error closing bufferedReader...");  
            }  
        }  
    }  

    body = stringBuilder.toString();  
}

@Override  
public ServletInputStream getInputStream () throws IOException {          
    final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());

    ServletInputStream inputStream = new ServletInputStream() {  
        public int read () throws IOException {  
            return byteArrayInputStream.read();  
        }  
    };

    return inputStream;  
} 
}
12
user219882

これは私のために働いた。 getInputStreamを実装します。

private class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private byte[] body;

    public MyHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        try {
            body = IOUtils.toByteArray(request.getInputStream());
        } catch (IOException ex) {
            body = new byte[0];
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new ServletInputStream() {
            ByteArrayInputStream bais = new ByteArrayInputStream(body);

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

}

次に、メソッドで使用します:

//copy body
servletRequest = new MyHttpServletRequestWrapper(servletRequest);
8
otaku

サーブレット3.1の場合

class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private byte[] body;

    public MyHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        try {
            body = IOUtils.toByteArray(request.getInputStream());
        } catch (IOException ex) {
            body = new byte[0];
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        return new DelegatingServletInputStream(new ByteArrayInputStream(body));


    }

}


public class DelegatingServletInputStream extends ServletInputStream {

    private final InputStream sourceStream;

    private boolean finished = false;


    /**
     * Create a DelegatingServletInputStream for the given source stream.
     *
     * @param sourceStream the source stream (never {@code null})
     */
    public DelegatingServletInputStream(InputStream sourceStream) {
        this.sourceStream = sourceStream;
    }

    /**
     * Return the underlying source stream (never {@code null}).
     */
    public final InputStream getSourceStream() {
        return this.sourceStream;
    }


    @Override
    public int read() throws IOException {
        int data = this.sourceStream.read();
        if (data == -1) {
            this.finished = true;
        }
        return data;
    }

    @Override
    public int available() throws IOException {
        return this.sourceStream.available();
    }

    @Override
    public void close() throws IOException {
        super.close();
        this.sourceStream.close();
    }

    @Override
    public boolean isFinished() {
        return this.finished;
    }

    @Override
    public boolean isReady() {
        return true;
    }

    @Override
    public void setReadListener(ReadListener readListener) {
        throw new UnsupportedOperationException();
    }

}
6
宏杰李

request.getInputStream()は1回だけ読み取りを許可されます。このメソッドを何度も使用するには、HttpServletReqeustWrapperクラスに追加のカスタムタスクを実行する必要があります。以下のサンプルラッパークラスを参照してください。

public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
    private ByteArrayOutputStream cachedBytes;

    public MultiReadHttpServletRequest(HttpServletRequest request) {
        super(request);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (cachedBytes == null)
            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
         */
        cachedBytes = new ByteArrayOutputStream();
        IOUtils.copy(super.getInputStream(), 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();
        }
    }
}

私の場合、すべての着信要求をログにトレースします。フィルターを作成しました

パブリッククラスTracerRequestFilterはFilter {private static final Logger LOG = LoggerFactory.getLogger(TracerRequestFilter.class);を実装します。

@Override
public void destroy() {

}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
        ServletException {
    final HttpServletRequest req = (HttpServletRequest) request;

    try {
        if (LOG.isDebugEnabled()) {
            final MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(req);
            // debug payload info
            logPayLoad(wrappedRequest);
            chain.doFilter(wrappedRequest, response);
        } else {
            chain.doFilter(request, response);
        }
    } finally {
        LOG.info("end-of-process");
    }
}

private String getRemoteAddress(HttpServletRequest req) {
    String ipAddress = req.getHeader("X-FORWARDED-FOR");
    if (ipAddress == null) {
        ipAddress = req.getRemoteAddr();
    }
    return ipAddress;
}

private void logPayLoad(HttpServletRequest request) {
    final StringBuilder params = new StringBuilder();
    final String method = request.getMethod().toUpperCase();
    final String ipAddress = getRemoteAddress(request);
    final String userAgent = request.getHeader("User-Agent");
    LOG.debug(String.format("============debug request=========="));
    LOG.debug(String.format("Access from ip:%s;ua:%s", ipAddress, userAgent));
    LOG.debug(String.format("Method : %s requestUri %s", method, request.getRequestURI()));
    params.append("Query Params:").append(System.lineSeparator());
    Enumeration<String> parameterNames = request.getParameterNames();

    for (; parameterNames.hasMoreElements();) {
        String paramName = parameterNames.nextElement();
        String paramValue = request.getParameter(paramName);
        if ("password".equalsIgnoreCase(paramName) || "pwd".equalsIgnoreCase(paramName)) {
            paramValue = "*****";
        }
        params.append("---->").append(paramName).append(": ").append(paramValue).append(System.lineSeparator());
    }
    LOG.debug(params.toString());
    /** request body */

    if ("POST".equals(method) || "PUT".equals(method)) {
        try {
            LOG.debug(IOUtils.toString(request.getInputStream()));
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }
    LOG.debug(String.format("============End-debug-request=========="));
}

@Override
public void init(FilterConfig arg0) throws ServletException {

}
}

Servlet 2.5と3.0の両方で動作します。フォームエンコードされたjson本体とリクエストjson本体の両方のすべてのリクエストパラメーターが表示されます。

1
sopheamak

サーブレットリクエストのinputStreamは、ストリームであるため一度しか使用できません。これを保存してからバイト配列から取得すると、解決できます。

public class HttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper {

private final byte[] body;

public HttpServletRequestWrapper(HttpServletRequest request)
        throws IOException {
    super(request);
    body = StreamUtil.readBytes(request.getReader(), "UTF-8");
}

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

@Override
public ServletInputStream getInputStream() throws IOException {
    final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
    return new ServletInputStream() {

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

        @Override
        public boolean isFinished() {
            return false;
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setReadListener(ReadListener arg0) {
        }
    };
}
}

フィルター内:

ServletRequest requestWrapper = new HttpServletRequestWrapper(request);
1
link