Java.lang.IllegalStateException: getReader () a déjà été appelé pour cette requête
je veux ajouter la journalisation à mon Servlet, donc J'ai créé un filtre qui devrait afficher la requête et aller au Servlet. Mais malheureusement j'ai encouragé l'exception:
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)
donc pour résoudre ce problème j'ai trouvé la solution avec Wrapper, mais ça ne marche pas. Que puis-je utiliser ou changer de code? Des idées?
[Myhttpservrequetrequestwrapper]
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();
}
}
[MyFilter]
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);
}
}
4 réponses
Ressemble le restlet framework a appelé getRequestEntityStream()
sur L'objet Request qui appelle à son tour sur la demande jette IllegalStateException
. La documentation de L'API Servlet pour getReader () et getInputStream () dit:
public java.io.BufferedReader getReader()
...
...
Throws:
java.lang.IllegalStateException - if getInputStream() method has been called on this request
public ServletInputStream getInputStream()
...
...
Throws:
java.lang.IllegalStateException - if the getReader() method has already been called for this request
D'après la documentation il semble que nous ne pouvons pas appeler à la fois getReader() et getInputStream() sur l'objet Request. Je vous suggère d'utiliser getInputStream()
plutôt que getReader()
dans votre enveloppe.
le problème principal est que vous ne pouvez pas lire l'entrée à la fois comme flux binaire et comme flux de caractères, même si l'un est appelé dans un filtre et l'autre dans le servlet.
autant que je puisse dire les servlets sont fondamentalement brisés à cet égard. Vous pouvez essayer de contourner ce problème comme indiqué ici mais cela provoque d'autres problèmes mystérieux quand d'autres choses essayent et travaillent avec elle.
effectivement, il suggère de cloner la requête, de lire le corps et puis dans la classe the cloned en supplantant les méthodes getReader et getInputStream pour retourner le truc déjà récupéré.
le code avec lequel j'ai fini était ceci:
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()));
}
}
quoi qu'il en soit cela semblait fonctionner correctement jusqu'à ce que nous nous rendions compte que le téléchargement d'un fichier depuis le navigateur ne fonctionnait pas. J'ai traversé les changements et j'ai découvert que c'était le coupable.
certaines personnes dans les commentaires de cet article disent que vous devez outrepasser les méthodes pour faire avec les paramètres mais n'expliquent pas comment faire cela.
par conséquent, j'ai vérifié s'il y avait une différence entre les deux requêtes. Cependant, après le clonage de la demande il avait des ensembles identiques de paramètres (la requête originale + cloné n'en avait aucun) ainsi qu'un ensemble identique d'en-têtes.
cependant, d'une certaine manière, la requête était en train d'être effectuée et de gâcher la compréhension de la requête plus loin dans la ligne - dans mon cas causant une erreur bizaare dans une bibliothèque (extdirectspring) où quelque chose essayait de lire le contenu comme Json. En retirant le code qui lisait le corps dans le filtre, il a fonctionné à nouveau.
mon code d'appel ressemblait à ceci:
@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);
}
}
j'ai omis le contenu de ParseExtDirectTargetFrom
mais il appelle getReader().
dans mon cas, le filtre fonctionnait pour toutes les autres requêtes, mais le comportement étrange dans ce cas m'a fait réaliser que quelque chose n'était pas tout à fait correct et que ce que j'essayais de faire (implémenter un comportement raisonnable de gestion des exceptions pour les tests) ne valait pas la peine de briser potentiellement les futures requêtes aléatoires (car je ne pouvais pas comprendre ce qui avait fait que la requête soit devenue cassé.)
il est également intéressant de noter que le code cassé est inévitable - j'ai supposé qu'il pourrait être quelque chose de printemps mais ServletRequest va tout le chemin vers le haut - thats tout ce que vous obtenez même si vous faisiez un servlet à partir de zéro en sous-classant HttpServlet
Ma recommandation serait celui - là:ne pas lire le corps de la requête dans un filtre. Vous allez ouvrir une boîte de pandore qui va provoquer d'étranges problèmes plus tard.
Utiliser ContentCachingRequestWrapper classe. Wrap HttpServletRequest in this will resolve issue