java.lang.IllegalStateException: No se puede (reenviar | sendRedirect | crear sesión) después de que se haya confirmado la respuesta

96

Este método arroja

java.lang.IllegalStateException: no se puede reenviar después de que se haya confirmado la respuesta

y no puedo detectar el problema. ¿Alguna ayuda?

    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
fuente
4
Es difícil verlo así, pero parece que ya ha enviado algo de salida antes de su reenvío. ¿Podría imprimir el código completo y comprobar si no tiene ningún filtro en su lugar?
Kartoch

Respuestas:

244

Un malentendido común entre los abridores es que piensan que la llamada de un forward(), sendRedirect()o sendError()mágicamente salir y "saltar" fuera del bloque de método, la presente ignorando el resto del código. Por ejemplo:

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

Por tanto, esto en realidad no es cierto. Ciertamente, no se comportan de manera diferente a cualquier otro método de Java ( System#exit()por supuesto, es de esperar ). Cuando el someConditionen el ejemplo anterior es truey que está llamando tanto, forward()después sendRedirect()o sendError()en la misma petición / respuesta, entonces la probabilidad es grande de que obtendrá la excepción:

java.lang.IllegalStateException: no se puede reenviar después de que se haya confirmado la respuesta

Si la ifdeclaración llama a forward()y luego está llamando a sendRedirect()o sendError(), se lanzará la siguiente excepción:

java.lang.IllegalStateException: no se puede llamar a sendRedirect () después de que se haya confirmado la respuesta

Para solucionar este problema, debe agregar una return;declaración después

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

... o para introducir un bloque else.

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

Para precisar la causa raíz en su código, simplemente busque cualquier línea que llame a forward(), sendRedirect()o sendError()sin salir del bloque de método u omitir el remanente del código. Esto puede estar dentro del mismo servlet antes de la línea de código particular, pero también en cualquier servlet o filtro que se haya llamado antes del servlet particular.

En caso de sendError()que su único propósito sea establecer el estado de respuesta, utilice setStatus()en su lugar.


Otra causa probable es que el servlet escribe en la respuesta mientras forward()se llama a will, o ha sido llamado con el mismo método.

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

El tamaño predeterminado del búfer de respuesta en la mayoría de los servidores es de 2 KB, por lo que si escribe más de 2 KB en él, se confirmará y forward()fallará de la misma manera:

java.lang.IllegalStateException: no se puede reenviar después de que se haya confirmado la respuesta

La solución es obvia, simplemente no escriba la respuesta en el servlet. Esa es la responsabilidad de JSP. Simplemente establezca un atributo de solicitud así request.setAttribute("data", "some string")y luego imprímalo en JSP así ${data}. Consulte también nuestra página wiki de Servlets para aprender a usar los Servlets de la manera correcta.


Otra causa probable es que el servlet escriba una descarga de archivo en la respuesta, después de lo cual, por ejemplo, forward()se llama a.

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

Técnicamente, esto no es posible. Necesitas eliminar la forward()llamada. El usuario final permanecerá en la página abierta actualmente. Si realmente tiene la intención de cambiar la página después de la descarga de un archivo, entonces necesita mover la lógica de descarga del archivo a la carga de la página de destino.


Otra causa probable es que los métodos forward(), sendRedirect()o sendError()se invocan a través del código Java incrustado en un archivo JSP en forma anticuada <% scriptlets %>, una práctica que se desaconsejó oficialmente desde 2001 . Por ejemplo:

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

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

El problema aquí es que JSP escribe internamente inmediatamente el texto de la plantilla (es decir, código HTML) a través de out.write("<!DOCTYPE html> ... etc ...")tan pronto como se encuentra. Por lo tanto, este es esencialmente el mismo problema que se explicó en la sección anterior.

La solución es obvia, simplemente no escriba código Java en un archivo JSP. Esa es la responsabilidad de una clase Java normal, como un servlet o un filtro. Consulte también nuestra página wiki de Servlets para aprender a usar los Servlets de la manera correcta.


Ver también:


Sin relación con su problema concreto, su código JDBC está perdiendo recursos. Arregle eso también. Para obtener sugerencias, consulte también ¿Con qué frecuencia se deben cerrar Connection, Statement y ResultSet en JDBC?

BalusC
fuente
2
¿Con un descanso te refieres break;? Eso significaría que el código estaba dentro de algún foro whilebucle en el que forward()se llamó repetidamente durante el bucle (lo que, por lo tanto, es incorrecto, debe llamar hacia adelante solo una vez DESPUÉS del bucle, o para deshacerse del bucle, ya que aparentemente no es necesario) .
BalusC
@BalusC ¿Tiene alguna idea sobre este problema relacionado? stackoverflow.com/questions/18658021/…
Confile
@confile: No hago Grails, pero según la pila de llamadas, todavía está realizando una forward()llamada mientras no debería estar haciendo eso. JSF, con el que estoy familiarizado, también lo hace a menos que llame explícitamente FacesContext#responseComplete(). Esta pregunta relacionada (que encontré usando las palabras clave "griales previenen la respuesta de procesamiento") puede ser útil: stackoverflow.com/questions/5708654/…
BalusC
@BalusC Grails es básicamente Java, pero el problema está relacionado con los Servlets. ¿Tiene alguna otra idea de lo que puedo hacer? Puse un retorno después de cada renderizado, redirigir y reenviar como sugirió.
confile el
@confile: Lo sé. Ya respondí la causa: Grails todavía está realizando una forward()llamada mientras no debería estar haciendo eso. La solución es funcionalmente obvia: dígale que no haga eso. Es decir, no tenía idea de que se había hecho cargo mediante programación del trabajo que se suponía que debía hacer Grails: manejar la respuesta. Técnicamente, no tengo idea de cómo decirle eso a Grails. Pero sé que muchos otros marcos MVC admiten esto (recibir instrucciones de no manejar la respuesta por sí mismos), como JSF, Spring MVC, Wicket, etc. Me sorprendería si esto fuera imposible en Grails.
BalusC
19

incluso agregar una declaración de retorno muestra esta excepción, para la cual la única solución es este código:

if(!response.isCommitted())
// Place another redirection
usuario1503117
fuente
6

Por lo general, ve este error después de haber realizado una redirección y luego intenta enviar más datos al flujo de salida. En los casos en los que he visto esto en el pasado, a menudo es uno de los filtros el que intenta redirigir la página y luego reenvía al servlet. No puedo ver nada inmediatamente incorrecto con el servlet, por lo que es posible que desee intentar echar un vistazo a los filtros que tenga instalados también.

Editar : algo más de ayuda para diagnosticar el problema ...

El primer paso para diagnosticar este problema es determinar exactamente dónde se lanza la excepción. Estamos asumiendo que está siendo arrojado por la línea.

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

Pero es posible que descubra que se lanza más adelante en el código, donde está tratando de enviar al flujo de salida después de haber intentado hacer el reenvío. Si proviene de la línea anterior, significa que en algún lugar antes de esta línea tiene:

  1. datos de salida al flujo de salida, o
  2. hecho otra redirección de antemano.

¡Buena suerte!

Paul Wagland
fuente
2

Esto se debe a que su servlet está intentando acceder a un objeto de solicitud que ya no existe. La instrucción forward o include de un servlet no detiene la ejecución del bloque de método. Continúa hasta el final del bloque de método o la primera declaración de retorno como cualquier otro método de Java.

La mejor manera de resolver este problema es simplemente configurar la página (donde se supone que debe reenviar la solicitud) dinámicamente de acuerdo con su lógica. Es decir:

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
}

y haz el avance solo una vez en la última línea ...

también puede solucionar este problema usando la declaración return después de cada forward () o poner cada forward () en if ... else block

Suman Sengupta
fuente
2

quite

        super.service(req, res);

Entonces funcionó bien para mi

kartikag01
fuente
2

Bache...

Solo tuve el mismo error. Me di cuenta de que estaba invocando super.doPost(request, response);al anular el doPost()método, así como al invocar explícitamente al constructor de superclase

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

Tan pronto como comenté la declaración super.doPost(request, response);interna doPost(), funcionó perfectamente ...

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

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

}

No hace falta decir que necesito volver a leer sobre las super()mejores prácticas: p

John Rambo
fuente
1

Debe agregar una declaración de devolución mientras reenvía o redirige el flujo.

Ejemplo:

si adelante,

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

si redirige,

    response.sendRedirect(roundTripURI);
    return;
Ashish Mishra
fuente
0

Después del método de retorno hacia adelante, simplemente puede hacer esto:

return null;

Romperá el alcance actual.

Amir Amiri
fuente