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);
    }
}
15
demandé sur smas 2011-09-06 14:45:54

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.

12
répondu Suresh Kumar 2011-09-06 11:03:19

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.

5
répondu Kennet 2011-09-06 11:04:11

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.

3
répondu Jonny Leeds 2014-07-31 13:10:20

Utiliser ContentCachingRequestWrapper classe. Wrap HttpServletRequest in this will resolve issue

0
répondu Swarit Agarwal 2017-07-03 09:22:01