java.lang.IllegalStateException: Cannot (forward | sendRedirect | create session) po zatwierdzeniu odpowiedzi

96

Ta metoda rzuca

java.lang.IllegalStateException: nie można przekazać dalej po zatwierdzeniu odpowiedzi

i nie jestem w stanie dostrzec problemu. Jakaś pomoc?

    int noOfRows = Integer.parseInt(request.getParameter("noOfRows"));
    String chkboxVal = "";
    // String FormatId=null;
    Vector vRow = new Vector();
    Vector vRow1 = new Vector();
    String GroupId = "";
    String GroupDesc = "";
    for (int i = 0; i < noOfRows; i++) {
        if ((request.getParameter("chk_select" + i)) == null) {
            chkboxVal = "notticked";
        } else {
            chkboxVal = request.getParameter("chk_select" + i);
            if (chkboxVal.equals("ticked")) {
                fwdurl = "true";
                Statement st1 = con.createStatement();
                GroupId = request.getParameter("GroupId" + i);
                GroupDesc = request.getParameter("GroupDesc" + i);
                ResultSet rs1 = st1
                        .executeQuery("select FileId,Description from cs2k_Files "
                                + " where FileId like 'M%' and co_code = "
                                + ccode);
                ResultSetMetaData rsm = rs1.getMetaData();
                int cCount = rsm.getColumnCount();

                while (rs1.next()) {
                    Vector vCol1 = new Vector();
                    for (int j = 1; j <= cCount; j++) {
                        vCol1.addElement(rs1.getObject(j));
                    }
                    vRow.addElement(vCol1);
                }
                rs1 = st1
                        .executeQuery("select FileId,NotAllowed from cs2kGroupSub "
                                + " where FileId like 'M%' and GroupId = '"
                                + GroupId + "'" + " and co_code = " + ccode);
                rsm = rs1.getMetaData();
                cCount = rsm.getColumnCount();

                while (rs1.next()) {
                    Vector vCol2 = new Vector();
                    for (int j = 1; j <= cCount; j++) {
                        vCol2.addElement(rs1.getObject(j));
                    }
                    vRow1.addElement(vCol2);
                }

                // throw new Exception("test");

                break;
            }
        }
    }
    if (fwdurl.equals("true")) {
        // throw new Exception("test");
        // response.sendRedirect("cs2k_GroupCopiedUpdt.jsp") ;
        request.setAttribute("GroupId", GroupId);
        request.setAttribute("GroupDesc", GroupDesc);
        request.setAttribute("vRow", vRow);
        request.setAttribute("vRow1", vRow1);
        getServletConfig().getServletContext().getRequestDispatcher(
                "/GroupCopiedUpdt.jsp").forward(request, response);
    }
sansknwoledge
źródło
4
Trudno to zobaczyć, ale wygląda na to, że wysłałeś już jakieś dane wyjściowe przed przejściem dalej. Czy mógłbyś wydrukować cały kod i sprawdzić, czy nie masz zainstalowanego żadnego filtra?
Kartoch

Odpowiedzi:

244

Powszechnym nieporozumieniem wśród początkujących jest to, że myślą, że wywołanie a forward(), sendRedirect()lub w sendError()magiczny sposób zakończyło się i „wyskoczyło” z bloku metody, ignorując tym samym pozostałość kodu. Na przykład:

protected void doXxx() {
    if (someCondition) {
        sendRedirect();
    }
    forward(); // This is STILL invoked when someCondition is true!
}

Tak więc w rzeczywistości nie jest to prawdą. Z pewnością nie zachowują się inaczej niż jakiekolwiek inne metody Java (spodziewaj się System#exit()oczywiście). Kiedy someConditionw powyższym przykładzie jest truei dzwonisz forward()po tym samym żądaniu / odpowiedzi sendRedirect()lub sendError()z tą samą odpowiedzią, istnieje duża szansa , że otrzymasz wyjątek:

java.lang.IllegalStateException: nie można przekazać dalej po zatwierdzeniu odpowiedzi

Jeśli ifinstrukcja wywołuje a, forward()a następnie wywołujesz sendRedirect()lub sendError(), zostanie zgłoszony poniższy wyjątek:

java.lang.IllegalStateException: nie można wywołać metody sendRedirect () po zatwierdzeniu odpowiedzi

Aby to naprawić, musisz return;później dodać oświadczenie

protected void doXxx() {
    if (someCondition) {
        sendRedirect();
        return;
    }
    forward();
}

... lub wprowadzić inny blok.

protected void doXxx() {
    if (someCondition) {
        sendRedirect();
    } else {
        forward();
    }
}

Aby usunąć główną przyczynę w kodzie, po prostu wyszukaj dowolną linię, która wywołuje a forward(), sendRedirect()lub sendError()bez wychodzenia z bloku metody lub pomijania pozostałej części kodu. Może to znajdować się w tym samym serwlecie przed określoną linią kodu, ale także w dowolnym aplecie lub filtrze, który został wywołany przed określonym serwletem.

W przypadku sendError(), jeśli jedynym celem jest ustawienie statusu odpowiedzi, użyj setStatus()zamiast tego.


Inną prawdopodobną przyczyną jest to, że serwlet zapisuje w odpowiedzi, podczas gdy forward()zostanie wywołane a, lub zostało wywołane tą samą metodą.

protected void doXxx() {
    out.write("some string");
    // ... 
    forward(); // Fail!
}

W większości serwerów rozmiar bufora odpowiedzi domyślnie wynosi 2 KB, więc jeśli napiszesz do niego więcej niż 2 KB, zostanie on forward()zatwierdzony i zakończy się niepowodzeniem w ten sam sposób:

java.lang.IllegalStateException: nie można przekazać dalej po zatwierdzeniu odpowiedzi

Rozwiązanie jest oczywiste, po prostu nie pisz odpowiedzi w serwlecie. Za to odpowiada strona JSP. Po prostu ustawiasz atrybut żądania w ten sposób, request.setAttribute("data", "some string")a następnie drukujesz go w JSP w ten sposób ${data}. Zobacz również naszą stronę wiki serwletów, aby dowiedzieć się, jak używać serwletów we właściwy sposób.


Inną prawdopodobną przyczyną jest to, że serwlet zapisuje pobranie pliku do odpowiedzi, po czym forward()wywoływana jest np . A.

protected void doXxx() {
    out.write(bytes);
    // ... 
    forward(); // Fail!
}

Nie jest to technicznie możliwe. Musisz usunąć forward()połączenie. Użytkownik końcowy pozostanie na aktualnie otwartej stronie. Jeśli faktycznie zamierzasz zmienić stronę po pobraniu pliku, musisz przenieść logikę pobierania plików na ładowanie strony docelowej.


Jeszcze inną prawdopodobną przyczyną jest to, że forward(), sendRedirect()lub sendError()metody są wywoływane za pomocą kodu Java osadzone w pliku JSP w postaci starej drodze <% scriptlets %>, praktyki, która została oficjalnie zniechęca od 2001 roku . Na przykład:

<!DOCTYPE html>
<html lang="en">
    <head>
        ... 
    </head>
    <body>
        ...

        <% sendRedirect(); %>
        
        ...
    </body>
</html>

Problem polega na tym, że JSP wewnętrznie zapisuje tekst szablonu (tj. Kod HTML) out.write("<!DOCTYPE html> ... etc ...")natychmiast po jego napotkaniu. Jest to zatem zasadniczo ten sam problem, co wyjaśniono w poprzedniej sekcji.

Rozwiązanie jest oczywiste, po prostu nie pisz kodu Java w pliku JSP. Za to odpowiada normalna klasa Javy, taka jak serwlet lub filtr. Zobacz także naszą stronę wiki serwletów, aby dowiedzieć się, jak używać serwletów we właściwy sposób.


Zobacz też:


Twój kod JDBC, niezwiązany z konkretnym problemem, przecieka zasoby. Napraw to również. Aby uzyskać wskazówki, zobacz także Jak często należy zamykać Connection, Statement i ResultSet w JDBC?

BalusC
źródło
2
Masz na myśli przerwę break;? Oznaczałoby to, że kod znajdował się w jakiejś pętli forlub whilepętli, w której forward()był wywoływany wielokrotnie podczas pętli (co jest zatem niepoprawne, powinieneś wywołać przekierowanie tylko raz PO pętli - lub pozbyć się pętli, ponieważ najwyraźniej nie jest potrzebna) .
BalusC
@BalusC Czy masz pomysł na ten powiązany problem? stackoverflow.com/questions/18658021/…
konfile
@confile: nie robię Grails, ale w oparciu o stos wywołań nadal wykonuje forward()wywołanie, podczas gdy nie powinno tego robić. JSF, który znam, robi to również, chyba że wyraźnie zadzwonisz FacesContext#responseComplete(). To pokrewne pytanie (które znalazłem przy użyciu słów kluczowych „grails zapobiegają renderowaniu odpowiedzi”) może być pomocne: stackoverflow.com/questions/5708654/ ...
BalusC
@BalusC Grails to w zasadzie Java, ale problem dotyczy serwletów. Czy masz inny pomysł, co mogę zrobić. Zwracam wartość po każdym renderowaniu, przekierowuję i przekierowuję zgodnie z sugestią.
konfile
@confile: Wiem. Odpowiedziałem już na przyczynę: Grails nadal wykonuje forward()połączenie, podczas gdy nie powinien tego robić. Rozwiązanie jest funkcjonalnie oczywiste: powiedz mu, żeby tego nie robił. Nie miał mianowicie pojęcia, że ​​programowo przejąłeś zadanie, które miał wykonać Grails: obsługę odpowiedzi. Technicznie nie mam pojęcia, jak to powiedzieć Grailsowi. Ale wiem, że wiele innych frameworków MVC obsługuje to (poinstruowane, aby same nie obsługiwały odpowiedzi), takich jak JSF, Spring MVC, Wicket itp. Byłbym zaskoczony, gdyby było to niemożliwe w Grails.
BalusC
19

nawet dodanie instrukcji return wywołuje ten wyjątek, dla którego jedynym rozwiązaniem jest ten kod:

if(!response.isCommitted())
// Place another redirection
user1503117
źródło
6

Zwykle pojawia się ten błąd po wykonaniu przekierowania, a następnie próbie wyprowadzenia większej ilości danych do strumienia wyjściowego. W przypadkach, w których widziałem to w przeszłości, często jest to jeden z filtrów, który próbuje przekierować stronę, a następnie nadal przekazuje ją do serwletu. Nie widzę od razu nic złego w serwlecie, więc możesz spróbować rzucić okiem na wszystkie filtry, które masz na miejscu.

Edycja : dodatkowa pomoc w diagnozowaniu problemu…

Pierwszym krokiem do zdiagnozowania tego problemu jest ustalenie, gdzie dokładnie jest zgłaszany wyjątek. Zakładamy, że jest rzucany przez linę

getServletConfig().getServletContext()
                  .getRequestDispatcher("/GroupCopiedUpdt.jsp")
                  .forward(request, response);

Ale może się okazać, że jest on wyrzucany później w kodzie, w którym próbujesz wyprowadzić do strumienia wyjściowego po próbie wykonania operacji do przodu. Jeśli pochodzi z powyższej linii, oznacza to, że gdzieś przed tą linią masz albo:

  1. dane wyjściowe do strumienia wyjściowego lub
  2. wykonałem wcześniej inne przekierowanie.

Powodzenia!

Paul Wagland
źródło
2

Dzieje się tak, ponieważ aplet próbuje uzyskać dostęp do obiektu żądania, który już nie istnieje. Instrukcja forward lub include serwletu nie zatrzymuje wykonywania bloku metody. Kontynuuje do końca bloku metody lub pierwszej instrukcji powrotu, tak jak każda inna metoda Java.

Najlepszym sposobem rozwiązania tego problemu jest po prostu ustawienie strony (na której ma zostać przekazane żądanie) dynamicznie zgodnie z logiką. To jest:

protected void doPost(request , response){
String returnPage="default.jsp";
if(condition1){
 returnPage="page1.jsp";
}
if(condition2){
   returnPage="page2.jsp";
}
request.getRequestDispatcher(returnPage).forward(request,response); //at last line
}

i wykonaj naprzód tylko raz w ostatniej linii ...

możesz również rozwiązać ten problem, używając instrukcji return po każdym forward () lub wstawiając każdą forward () w bloku if ... else

Suman Sengupta
źródło
2

usunąłem

        super.service(req, res);

Wtedy działało dobrze dla mnie

kartikag01
źródło
2

Guz...

Po prostu miałem ten sam błąd. Zauważyłem, że wywoływałem super.doPost(request, response);podczas zastępowania doPost()metody, a także jawnie wywoływałem konstruktora nadklasy

    public ScheduleServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

Jak tylko skomentowałem oświadczenie super.doPost(request, response);od wewnątrz doPost(), zadziałało idealnie ...

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //super.doPost(request, response);
        // More code here...

}

Nie trzeba dodawać, że muszę ponownie przeczytać super()najlepsze praktyki: s

John Rambo
źródło
1

Należy dodać instrukcję return podczas przekazywania lub przekierowywania przepływu.

Przykład:

jeśli forwardind,

    request.getRequestDispatcher("/abs.jsp").forward(request, response);
    return;

jeśli przekierowujesz,

    response.sendRedirect(roundTripURI);
    return;
Ashish Mishra
źródło
0

Po metodzie powrotu do przodu możesz po prostu zrobić to:

return null;

Spowoduje to przerwanie obecnego zakresu.

Amir Amiri
źródło