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

Resuelto sansknwoledge asked hace 14 años • 9 respuestas

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 avatar Jan 23 '10 21:01 sansknwoledge
Aceptado

forward// sendRedirect¡ sendErrorNO salgas del método!

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

protected void doXxx(...) {
    if (someCondition) {
        response.sendRedirect(...);
    }

    dispatcher.forward(...); // This is STILL invoked when someCondition is true!
}

O en un filtro:

public void doFilter(...) {
    if (someCondition) {
        response.sendRedirect(...);
    }

    chain.doFilter(...); // This is STILL invoked when someCondition is true!
}

Por lo tanto, esto en realidad no es cierto. Ciertamente no se comportan de manera diferente a otros métodos Java (eso sí, System#exit()por supuesto). Cuando el someConditionejemplo anterior es truey usted está llamando forward()o doFilter()después sendRedirect()o sendError()en la misma solicitud/respuesta, entonces hay grandes posibilidades de que obtenga la excepción:

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

Esto también se aplica a la condición inversa. Si la ifdeclaración llama a forward()y luego llamas sendRedirect()o sendError(), entonces se puede generar 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) {
        response.sendRedirect(...);
        return;
    }

    dispatcher.forward(...);
}

... o para introducir un bloque else.

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

Para identificar 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 ni omitir el resto 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 haya sido llamado antes de ese servlet particular.

En caso de sendError(), si su único propósito es establecer el estado de la respuesta, utilícelo setStatus()en su lugar.

No escribas ninguna cadena antes forward//sendRedirectsendError

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

protected void doXxx() {
    out.write("<p>some html</p>");

    // ...

    dispatcher.forward(); // Fail!
}

El tamaño del búfer de respuesta predeterminado en la mayoría de los servidores es 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 en la respuesta en el servlet. Esa es responsabilidad de la JSP. Simplemente establece un atributo de solicitud así request.setAttribute("data", "some string")y luego lo imprime en JSP así ${data}. Consulte también nuestra página wiki de Servlets para aprender a utilizar los Servlets de la manera correcta.

No escribas ningún archivo antes forward//sendRedirectsendError

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

protected void doXxx() {
    out.write(bytes);

    // ...
 
    dispatcher.forward(); // Fail!
}

Esto técnicamente 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 debe mover la lógica de descarga del archivo a la carga de la página de destino. Básicamente: primero cree un archivo temporal en el disco usando la forma mencionada en esta respuesta Cómo guardar el archivo generado temporalmente en una aplicación web basada en servlet , luego envíe una redirección con el nombre/identificador del archivo como parámetro de solicitud y en la página de destino imprima condicionalmente. en presencia de ese parámetro de solicitud <script>window.location='...';</script>que descarga inmediatamente el archivo temporal a través de una de las formas mencionadas en esta respuesta La forma más sencilla de servir datos estáticos desde fuera del servidor de aplicaciones en una aplicación web Java .

No llamar // forwarden JSPsendRedirectsendError

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

<!DOCTYPE html>
<html lang="en">
    <head>
        ... 
    </head>
    <body>
        ...
        <% response.sendRedirect(...); %>
        ...
    </body>
</html>

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

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

Ver también:

  • ¿Qué significa exactamente "Respuesta ya comprometida"? ¿Cómo manejar las excepciones entonces?

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

BalusC avatar Jan 23 '2010 22:01 BalusC

Incluso agregar una declaración de devolución genera esta excepción, para la cual la única solución es este código:

if(!response.isCommitted())
// Place another redirection
user1503117 avatar Feb 23 '2013 19:02 user1503117

Por lo general, verá este error después de haber realizado una redirección y luego intentar 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 aún así la reenvía al servlet. No veo nada malo inmediatamente en el servlet, por lo que es posible que desees intentar echar un vistazo a los filtros que tengas implementados también.

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

El primer paso para diagnosticar este problema es determinar exactamente dónde se produce la excepción. Estamos asumiendo que está siendo lanzado 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 intenta enviar al flujo de salida después de haber intentado realizar el reenvío. Si proviene de la línea anterior, significa que en algún lugar antes de esta línea tienes:

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

¡Buena suerte!

Paul Wagland avatar Jan 23 '2010 14:01 Paul Wagland