java.lang.IllegalStateException: Impossibile (inoltra | sendRedirect | creare sessione) dopo che la risposta è stata confermata


96

Questo metodo genera

java.lang.IllegalStateException: impossibile inoltrare dopo che è stato eseguito il commit della risposta

e non sono in grado di individuare il problema. Qualsiasi aiuto?

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

4
È difficile da vedere in questo modo, ma sembra che tu abbia già inviato un output prima del tuo forward. Potresti stampare il codice completo e controllare se non hai alcun filtro in atto?
Kartoch

Risposte:


244

Un malinteso comune tra i principianti è che pensano che la chiamata di a forward(), sendRedirect()o sendError()esca magicamente e "salta" fuori dal blocco del metodo, ignorando così il resto del codice. Per esempio:

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

Questo non è quindi effettivamente vero. Certamente non si comportano in modo diverso da qualsiasi altro metodo Java (aspettatevi System#exit()ovviamente). Quando il someConditionnell'esempio sopra è truee si sta chiamando così forward()dopo sendRedirect()o sendError()sulla stessa richiesta / risposta, allora la probabilità è grande che si otterrà l'eccezione:

java.lang.IllegalStateException: impossibile inoltrare dopo che è stato eseguito il commit della risposta

Se l' ifistruzione chiama a forward()e successivamente stai chiamando sendRedirect()o sendError(), verrà generata l'eccezione seguente:

java.lang.IllegalStateException: impossibile chiamare sendRedirect () dopo che la risposta è stata confermata

Per risolvere questo problema, è necessario aggiungere return;successivamente un'istruzione

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

... o per introdurre un altro blocco.

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

Per individuare la causa principale nel tuo codice, cerca una qualsiasi riga che chiami a forward(), sendRedirect()osendError() senza uscire dal blocco del metodo o saltare il resto del codice. Questo può essere all'interno dello stesso servlet prima della particolare riga di codice, ma anche in qualsiasi servlet o filtro che è stato chiamato prima del particolare servlet.

In caso di sendError(), se il tuo unico scopo è impostare lo stato della risposta, usa setStatus()invece.


Un'altra probabile causa è che il servlet scrive nella risposta mentre a forward()verrà chiamato, o è stato chiamato nello stesso metodo.

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

La dimensione del buffer di risposta è predefinita nella maggior parte dei server su 2 KB, quindi se scrivi più di 2 KB, verrà eseguito il commit e forward()fallirà allo stesso modo:

java.lang.IllegalStateException: impossibile inoltrare dopo che è stato eseguito il commit della risposta

La soluzione è ovvia, basta non scrivere nella risposta nel servlet. Questa è la responsabilità del JSP. Devi solo impostare un attributo di richiesta in questo modo request.setAttribute("data", "some string")e quindi stamparlo in JSP in questo modo ${data}. Vedi anche la nostra pagina wiki Servlet per imparare a usare i Servlet nel modo giusto.


Un'altra probabile causa è che il servlet scrive un file scaricato nella risposta, dopodiché forward()viene chiamato ad esempio a .

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

Questo tecnicamente non è possibile. Devi rimuovere la forward()chiamata. L'utente finale rimarrà sulla pagina attualmente aperta. Se si intende effettivamente modificare la pagina dopo il download di un file, è necessario spostare la logica di download del file nel caricamento della pagina della pagina di destinazione.


Ancora un altro causa probabile è che il forward(), sendRedirect()o sendError()metodi vengono richiamati tramite codice Java incorporato in un file JSP in forma di vecchia maniera <% scriptlets %>, una pratica che è stata ufficialmente scoraggiato dal 2001 . Per esempio:

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

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

Il problema qui è che JSP scrive internamente immediatamente il testo del modello (cioè il codice HTML) tramite out.write("<!DOCTYPE html> ... etc ...")non appena viene rilevato. Questo è quindi essenzialmente lo stesso problema spiegato nella sezione precedente.

La soluzione è ovvia, basta non scrivere codice Java in un file JSP. Questa è la responsabilità di una normale classe Java come un servlet o un filtro. Vedi anche la nostra pagina wiki Servlet per imparare a usare i Servlet nel modo giusto.


Guarda anche:


Non correlato al tuo problema concreto, il tuo codice JDBC perde risorse. Risolvi anche quello. Per suggerimenti, vedere anche Con quale frequenza devono essere chiusi Connection, Statement e ResultSet in JDBC?


2
Con una pausa intendi break;? Ciò significherebbe che il codice è stato stato dentro un po ' foro whilead anello in cui il forward()è stato chiamato più volte durante il ciclo (che è quindi corretto, si dovrebbe chiamare in avanti solo una volta dopo il ciclo --o per sbarazzarsi del ciclo come è a quanto pare non necessario) .
BalusC

@BalusC Hai un'idea su questo problema correlato? stackoverflow.com/questions/18658021/...
confile

@confile: non faccio Grails, ma in base allo stack di chiamate, sta ancora eseguendo una forward()chiamata mentre non dovrebbe farlo. JSF, con cui ho familiarità, lo fa anche a meno che tu non chiami esplicitamente FacesContext#responseComplete(). Questa domanda relativa (che ho trovato utilizzando le parole chiave "graal impediscono render risposta") può essere utile: stackoverflow.com/questions/5708654/...
BalusC

@BalusC Grails è fondamentalmente Java, ma il problema è legato ai Servlet. Hai qualche altra idea di cosa posso fare. Metto un ritorno dopo ogni rendering, reindirizzamento e inoltro come suggerito.
Confile

@confile: lo so. Ho già risposto alla causa: Grails sta ancora effettuando una forward()chiamata mentre non dovrebbe farlo. La soluzione è funzionalmente ovvia: digli di non farlo. In particolare, non aveva idea che tu avessi assunto programmaticamente il lavoro che Grails avrebbe dovuto fare: gestire la risposta. Tecnicamente, non ho idea di come dirlo a Grails. Ma so che molti altri framework MVC lo supportano (essendo istruiti a non gestire la risposta da soli), come JSF, Spring MVC, Wicket, ecc. Sarei sorpreso se questo fosse impossibile in Grails.
BalusC

19

anche l'aggiunta di un'istruzione return fa apparire questa eccezione, per la quale l'unica soluzione è questo codice:

if(!response.isCommitted())
// Place another redirection

6

In genere viene visualizzato questo errore dopo aver già eseguito un reindirizzamento e quindi provare a inviare altri dati al flusso di output. Nei casi in cui l'ho visto in passato, spesso è uno dei filtri che sta tentando di reindirizzare la pagina, e poi ancora inoltra al servlet. Non riesco a vedere immediatamente nulla di sbagliato nel servlet, quindi potresti provare a dare un'occhiata anche ai filtri che hai in atto.

modificare : ulteriore aiuto nella diagnosi del problema ...

Il primo passo per diagnosticare questo problema è accertare esattamente dove viene generata l'eccezione. Supponiamo che venga lanciato dalla linea

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

Ma potresti scoprire che viene lanciato più avanti nel codice, dove stai cercando di eseguire l'output nel flusso di output dopo aver provato a eseguire l'inoltro. Se proviene dalla riga sopra, significa che da qualche parte prima di questa riga hai:

  1. dati di output nel flusso di output, o
  2. fatto un altro reindirizzamento in anticipo.

In bocca al lupo!


2

Questo perché il tuo servlet sta tentando di accedere a un oggetto richiesta che non esiste più. L'istruzione forward o include di un servlet non interrompe l'esecuzione del blocco del metodo. Continua fino alla fine del blocco del metodo o della prima istruzione di ritorno proprio come qualsiasi altro metodo java.

Il modo migliore per risolvere questo problema è semplicemente impostare la pagina (dove supponi di inoltrare la richiesta) dinamicamente secondo la tua logica. Questo è:

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
}

e fai l'attaccante solo una volta sull'ultima riga ...

puoi anche risolvere questo problema usando l'istruzione return dopo ogni forward () o inserire ogni forward () nel blocco if ... else


2

Ho rimosso

        super.service(req, res);

Quindi ha funzionato bene per me


2

Bump ...

Ho appena avuto lo stesso errore. Ho notato che stavo invocando super.doPost(request, response);durante l'override del doPost()metodo e invocando esplicitamente il costruttore della superclasse

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

Non appena ho commentato l' affermazione super.doPost(request, response);dall'interno doPost()ha funzionato perfettamente ...

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

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

}

Inutile dire che ho bisogno di rileggere le super()migliori pratiche: p


1

Dovresti aggiungere il ritorno istruzione mentre si inoltra o si reindirizza il flusso.

Esempio:

se forwardind,

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

in caso di reindirizzamento,

    response.sendRedirect(roundTripURI);
    return;

0

Dopo il metodo di ritorno in avanti puoi semplicemente fare questo:

return null;

Spezzerà l'ambito attuale.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.