私はこのコードを持っています:
_@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)
_
おそらくgetReader()
を使用してHttpServletRequestの使用を開始します。
_String ba = getBaId(getBody(httpRequest));
_
サーブレットは同じリクエストでgetInputStream()
を呼び出そうとしますが、これは許可されていません。行う必要があるのは、 ServletRequestWrapper
を使用してリクエストの本文のコピーを作成し、複数のメソッドで読み取ることができるようにすることです。私は完全な例を知っている時間がありません...知っています...申し訳ありません...
受け入れられた回答に基づく作業コード。
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;
}
}
これは私のために働いた。 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);
サーブレット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();
}
}
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本体の両方のすべてのリクエストパラメーターが表示されます。
サーブレットリクエストの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);