wake-up-neo.net

java.lang.IllegalStateException: getReader () wurde bereits für diese Anforderung aufgerufen

Ich möchte meinem Servlet die Protokollierung hinzufügen. Daher habe ich einen Filter erstellt, der die Anforderung anzeigen und zum Servlet gehen soll. Aber leider bin ich auf Ausnahmen gestoßen:

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)

Um dieses Problem zu beheben, habe ich eine Lösung mit Wrapper gefunden, die jedoch nicht funktioniert. Was kann ich sonst noch im Code verwenden/ändern? Irgendwelche Ideen?

[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();
    }
}

[MeinFilter]

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);
    }
}
18
smas

Sieht aus, als hätte das Restlet-Framework getRequestEntityStream() für das Request-Objekt aufgerufen, das wiederum getInputStream() aufruft, so dass der Aufruf von getReader() für die Anforderung IllegalStateException auslöst. Die Servlet-API-Dokumentation für getReader () und getInputStream () sagt:

 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

In der Dokumentation scheint es, dass wir nicht sowohl getReader () als auch getInputStream () für das Request-Objekt aufrufen können. Ich schlage vor, dass Sie getInputStream() anstelle von getReader() in Ihrem Wrapper verwenden.

13
Suresh Kumar

Das Hauptproblem ist, dass Sie die Eingabe nicht sowohl als Binärstrom als auch als Zeichenstrom lesen können, auch wenn der eine in einem Filter und der andere im Servlet aufgerufen wird. 

5
Kennet

Soweit ich das beurteilen kann, sind Servlets in dieser Hinsicht grundlegend defekt. Sie können versuchen, dieses Problem wie beschrieben umzugehen hier , aber das verursacht andere mysteriöse Probleme, wenn andere Dinge versuchen und damit arbeiten.

Er schlägt vor, die Anforderung zu klonen, den Text zu lesen und dann in der geklonten Klasse die Methoden getReader und getInputStream zu überschreiben, um das bereits abgerufene Material zurückzugeben.

Der Code, mit dem ich endete, war folgender:

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()));
    }
}

Trotzdem schien dies gut zu funktionieren, bis wir merkten, dass das Hochladen einer Datei über den Browser nicht funktionierte. Ich habe mich durch die Veränderungen halbiert und herausgefunden, dass dies der Schuldige war.

Einige Leute in den Kommentaren dieses Artikels sagen, dass Sie Methoden überschreiben müssen, die mit Parametern zu tun haben, aber nicht erklären, wie das geht. 

Als Ergebnis überprüfte ich, ob es einen Unterschied zwischen den beiden Anforderungen gab. Nach dem Klonen der Anforderung verfügte sie jedoch über identische Parametersätze (beide ursprünglichen Anforderungen + geklonte hatten keine) sowie identische Kopfzeilen. 

In gewisser Weise wurde jedoch die Anfrage ausgeführt und das Verständnis der Anfrage wurde weiter verfeinert - in meinem Fall verursachte sie einen bizaare-Fehler in einer Bibliothek (extdirectspring), wo etwas versuchte, den Inhalt als Json zu lesen. Durch Herausnehmen des Codes, der den Körper im Filter gelesen hat, ist er wieder funktionsfähig.

Mein Code sah so aus:

@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);
    }
}

Ich habe den Inhalt von ParseExtDirectTargetFrom ausgelassen, ruft jedoch getReader () auf.

In meinem Fall funktionierte der Filter für alle anderen Anforderungen, aber aufgrund des merkwürdigen Verhaltens in diesem Fall wurde mir klar, dass etwas nicht in Ordnung war und was ich zu tun versuchte (vernünftiges Verhalten bei Ausnahmebehandlung für Tests implementieren), war es nicht wert, willkürlich zu brechen zukünftige Anfragen (da ich nicht herausfinden konnte, warum die Anfrage beschädigt wurde).

Es ist auch erwähnenswert, dass der gebrochene Code unvermeidlich ist - ich nahm an, dass es etwas vom Frühling sein könnte, aber ServletRequest geht den ganzen Weg nach oben - das ist alles, was Sie auch bekommen, wenn Sie ein Servlet von Grund auf durch Unterklassen erstellen würden. HttpServlet

Meine Empfehlung wäre dies - Bitte nicht den Anfragetext in einem Filter lesen . Sie öffnen eine Dose Würmer, die später merkwürdige Probleme verursachen werden.

4
JonnyRaa

Verwenden Sie ContentCachingRequestWrapper class. Schließen Sie HttpServletRequest in das Problem ein

0
Swarit Agarwal