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.