java.lang.IllegalStateException:已为此请求调用getReader()
我想将日志添加到我的Servlet中,所以我创建了一个过滤器,它应该显示请求并转到Servlet。但不幸的是,我遇到了一个例外:java.lang.IllegalStateException:已为此请求调用getReader(),java,servlets,servlet-filters,Java,Servlets,Servlet Filters,我想将日志添加到我的Servlet中,所以我创建了一个过滤器,它应该显示请求并转到Servlet。但不幸的是,我遇到了一个例外: java.lang.IllegalStateException: getReader() has already been called for this request at org.apache.catalina.connector.Request.getInputStream(Request.java:948) at org.apache.cat
java.lang.IllegalStateException: getReader() has already been called for this request
at org.apache.catalina.connector.Request.getInputStream(Request.java:948)
at org.apache.catalina.connector.RequestFacade.getInputStream(RequestFacade.java:338)
at com.noelios.restlet.ext.servlet.ServletCall.getRequestEntityStream(ServletCall.java:190)
所以为了解决这个问题,我找到了使用包装器的解决方案,但它不起作用。我还可以在代码中使用/更改什么?有什么想法吗
[MyHttpServletRequestWrapper]
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper
{
public MyHttpServletRequestWrapper(HttpServletRequest request)
{
super(request);
}
private String getBodyAsString()
{
StringBuffer buff = new StringBuffer();
buff.append(" BODY_DATA START [ ");
char[] charArr = new char[getContentLength()];
try
{
BufferedReader reader = new BufferedReader(getReader());
reader.read(charArr, 0, charArr.length);
reader.close();
}
catch (IOException e)
{
e.printStackTrace();
}
buff.append(charArr);
buff.append(" ] BODY_DATA END ");
return buff.toString();
}
public String toString()
{
return getBodyAsString();
}
}
[我的过滤器]
public class MyFilterimplements Filter
{
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
final HttpServletRequestWrapper requestWrapper = new MyHttpServletRequestWrapper(httpServletRequest);
final String requestBody = requestWrapper.toString();
chain.doFilter(request, response);
}
}
看起来restlet框架对请求对象调用了
getRequestEntityStream()
,而请求对象又调用了getInputStream()
,因此对请求调用getReader()
会抛出IllegalStateException
。getReader()和getInputStream()的Servlet API文档说明:
从文档中可以看出,我们似乎无法对请求对象同时调用getReader()和getInputStream()。我建议您在包装器中使用
getInputStream()
而不是getReader()
。主要问题是,您无法将输入同时作为二进制流和字符流读取,即使在过滤器中调用一个,在servlet中调用另一个也是如此 据我所知,Servlet在这方面已经彻底崩溃。您可以尝试解决这个问题,但当其他事物尝试解决它时,会导致其他神秘的问题
实际上,他建议克隆请求,读取主体,然后在克隆的类中重写getReader和getInputStream方法以返回已经检索到的内容
我最终得到的代码是:
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
//this class stops reading the request payload twice causing an exception
public class WrappedRequest extends HttpServletRequestWrapper
{
private String _body;
private HttpServletRequest _request;
public WrappedRequest(HttpServletRequest request) throws IOException
{
super(request);
_request = request;
_body = "";
try (BufferedReader bufferedReader = request.getReader())
{
String line;
while ((line = bufferedReader.readLine()) != null)
_body += line;
}
}
@Override
public ServletInputStream getInputStream() throws IOException
{
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(_body.getBytes());
return new ServletInputStream()
{
public int read() throws IOException
{
return byteArrayInputStream.read();
}
};
}
@Override
public BufferedReader getReader() throws IOException
{
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
不管怎么说,在我们意识到从浏览器上传文件不起作用之前,这似乎工作正常。我把这些变化一分为二,发现这就是罪魁祸首
在那篇文章的评论中,有些人说需要重写方法来处理参数,但没有解释如何做到这一点
因此,我检查了两个请求是否有任何差异。但是,在克隆请求之后,它具有相同的参数集(原始请求+克隆的请求都没有),以及相同的头集
然而,在某种程度上,请求受到了影响,并进一步破坏了对请求的理解——在我的例子中,在一个库(extdirectspring)中导致了一个bizaare错误,其中有人试图将内容读取为Json。取出过滤器中读取主体的代码使其再次工作
我的呼叫代码如下所示:
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException
{
HttpServletRequest properRequest = ((HttpServletRequest)request);
String pathInfo = properRequest.getPathInfo();
String target = "";
if(pathInfo == null)
pathInfo = "";
if(pathInfo.equals("/router"))
{
//note this is because servlet requests hate you!
//if you read their contents more than once then they throw an exception so we need to do some madness
//to make this not the case
WrappedRequest wrappedRequest = new WrappedRequest(properRequest);
target = ParseExtDirectTargetFrom(wrappedRequest);
request = wrappedRequest;
}
boolean callingSpecialResetMethod = pathInfo.equals("/resetErrorState") || target.equals("resetErrorState");
if(_errorHandler.IsRejectingRequests() && !callingSpecialResetMethod)
return;
try {
filterChain.doFilter(request, response);
}
catch (Exception exception) {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "ERROR");
_errorHandler.NotifyOf(exception);
}
}
我已经编写了ParseExtDirectTargetFrom
的内容,但它调用了getReader()
在我的例子中,过滤器适用于所有其他请求,但这种奇怪的行为让我意识到有些地方不太正确,我试图做的(为测试实现合理的异常处理行为)不值得潜在地破坏未来的随机请求(因为我不知道是什么原因导致请求被破坏)
另外,值得注意的是,坏代码是不可避免的——我假设它可能来自spring,但ServletRequest一直在上升——即使您是通过子类化从头开始创建servlet,您也只能得到这些
我的建议是-不要在筛选器中读取请求正文。您将打开一罐蠕虫,稍后会导致奇怪的问题。使用ContentCachingRequestWrapper类。将HttpServletRequest包装在其中可以解决问题 示例:如果您想转换“HttpServletRequest servletRequest”,您可以执行以下操作
import org.springframework.web.util.ContentCachingRequestWrapper;
ContentCachingRequestWrapper request = new ContentCachingRequestWrapper(servletRequest);
希望对你有帮助
import org.springframework.web.util.ContentCachingRequestWrapper;
ContentCachingRequestWrapper request = new ContentCachingRequestWrapper(servletRequest);