java.lang.IllegalStateException: Não é possível (encaminhar | sendRedirect | criar sessão) após a resposta ter sido confirmada


96

Este método lança

java.lang.IllegalStateException: Não é possível encaminhar após a confirmação da resposta

e não consigo identificar o problema. Qualquer ajuda?

    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
É difícil ver assim, mas parece que você já enviou alguma saída antes de encaminhar. Você poderia imprimir o código completo e verificar se não há nenhum filtro instalado?
Kartoch

Respostas:


244

Um mal-entendido comum entre os iniciantes é que eles pensam que a chamada de a forward(), sendRedirect()ou sendError()iria sair magicamente e "pular" para fora do bloco de método, ignorando assim o resto do código. Por exemplo:

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

Portanto, isso não é verdade. Eles certamente não se comportam de maneira diferente de quaisquer outros métodos Java (espere, é System#exit()claro). Quando o someConditionexemplo acima é truee você está chamando forward()depois sendRedirect()ou sendError()na mesma solicitação / resposta, então a chance é grande de que você obtenha a exceção:

java.lang.IllegalStateException: Não é possível encaminhar após a confirmação da resposta

Se a ifinstrução chamar a forward()e você depois chamar sendRedirect()ou sendError(), a exceção abaixo será lançada:

java.lang.IllegalStateException: Não é possível chamar sendRedirect () após a resposta ter sido confirmada

Para corrigir isso, você precisa adicionar uma return;declaração depois

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

... ou para introduzir um bloco else.

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

Para Naildown a causa raiz em seu código, basta procurar por qualquer linha que chama uma forward(), sendRedirect()ou sendError()sem sair do bloco de método ou pular o resto do código. Isso pode estar dentro do mesmo servlet antes da linha de código específica, mas também em qualquer servlet ou filtro que foi chamado antes do servlet específico.

No caso de sendError(), se o seu único propósito é definir o status da resposta, use setStatus().


Outra causa provável é que o servlet grava na resposta enquanto um forward()será chamado ou foi chamado no mesmo método.

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

O tamanho padrão do buffer de resposta na maioria dos servidores é 2 KB, portanto, se você gravar mais de 2 KB nele, ele será confirmado e forward()falhará da mesma maneira:

java.lang.IllegalStateException: Não é possível encaminhar após a confirmação da resposta

A solução é óbvia, apenas não escreva para a resposta no servlet. Isso é responsabilidade do JSP. Você apenas define um atributo de solicitação como tal request.setAttribute("data", "some string")e, em seguida, imprime em JSP dessa forma ${data}. Veja também nossa página wiki Servlets para aprender como usar Servlets da maneira certa.


Outra causa provável é que o servlet grava um download de arquivo na resposta após o qual, por exemplo, a forward()é chamado.

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

Isso não é tecnicamente possível. Você precisa remover a forward()chamada. O usuário final permanecerá na página aberta no momento. Se você realmente pretende alterar a página após o download de um arquivo, será necessário mover a lógica de download do arquivo para o carregamento da página da página de destino.


Ainda outra causa provável é que os métodos forward(), sendRedirect()ou sendError()são invocados via código Java embutido em um arquivo JSP na forma antiga <% scriptlets %>, uma prática que foi oficialmente desencorajada desde 2001 . Por exemplo:

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

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

O problema aqui é que o JSP grava internamente imediatamente o texto do modelo (ou seja, código HTML) out.write("<!DOCTYPE html> ... etc ...")assim que é encontrado. Este é, portanto, essencialmente o mesmo problema explicado na seção anterior.

A solução é óbvia, apenas não escreva código Java em um arquivo JSP. Essa é a responsabilidade de uma classe Java normal, como um Servlet ou um Filtro. Veja também nossa página wiki Servlets para aprender como usar Servlets da maneira certa.


Veja também:


Não relacionado ao seu problema concreto, seu código JDBC está perdendo recursos. Corrija isso também. Para obter dicas, consulte também Com que frequência Connection, Statement e ResultSet devem ser fechados no JDBC?


2
Com uma pausa, você quer dizer break;? Isso significaria que o código estava dentro de algum forou whileloop em que forward()foi chamado repetidamente durante o loop (o que é, portanto, incorreto, você deve chamar o encaminhamento apenas uma vez APÓS o loop --ou para se livrar do loop, pois aparentemente não é necessário) .
BalusC

@BalusC Você tem uma ideia sobre esse problema relacionado? stackoverflow.com/questions/18658021/…
confile

@confile: Eu não faço Grails, mas baseado na pilha de chamadas, ele ainda está realizando uma forward()chamada, mas não deveria. O JSF, com o qual estou familiarizado, também faz isso, a menos que você chame explicitamente FacesContext#responseComplete(). Esta pergunta relacionada (que encontrei usando as palavras-chave "grails prevent render response") pode ser útil: stackoverflow.com/questions/5708654/…
BalusC

@BalusC Grails é basicamente Java, mas o problema está relacionado aos Servlets. Você tem alguma outra ideia do que posso fazer. Eu coloco um retorno após cada render, redireciono e encaminha como você sugeriu.
confile

@confile: eu sei. Já respondi à causa: Grails ainda está realizando uma forward()chamada, mas não deveria estar fazendo isso. A solução é funcionalmente óbvia: diga a ele para não fazer isso. Ele não fazia ideia de que você assumiu programaticamente o trabalho que Grails deveria fazer: lidar com a resposta. Tecnicamente, não tenho ideia de como dizer isso a Grails. Mas eu sei que muitos outros frameworks MVC suportam isso (sendo instruídos a não lidar com a resposta por si só), como JSF, Spring MVC, Wicket, etc. Eu ficaria surpreso se isso fosse impossível em Grails.
BalusC de

19

até mesmo adicionar uma instrução de retorno traz essa exceção, para a qual a única solução é este código:

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

6

Normalmente, você vê esse erro depois de já ter feito um redirecionamento e, em seguida, tentar enviar mais alguns dados para o fluxo de saída. Nos casos em que já vi isso, geralmente é um dos filtros que está tentando redirecionar a página e, em seguida, ainda encaminha para o servlet. Não consigo ver nada de errado com o servlet, então você pode tentar dar uma olhada em todos os filtros que você tem no lugar também.

Edit : Um pouco mais de ajuda no diagnóstico do problema ...

A primeira etapa para diagnosticar esse problema é verificar exatamente onde a exceção está sendo lançada. Estamos assumindo que está sendo jogado pela linha

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

Mas você pode descobrir que ele está sendo lançado posteriormente no código, onde está tentando enviar para o fluxo de saída depois de tentar fazer o encaminhamento. Se vier da linha acima, significa que em algum lugar antes dessa linha você tem:

  1. dados de saída para o fluxo de saída, ou
  2. fez outro redirecionamento com antecedência.

Boa sorte!


2

Isso ocorre porque seu servlet está tentando acessar um objeto de solicitação que não existe mais. A instrução de encaminhamento ou inclusão de um servlet não interrompe a execução do bloco de método. Ele continua até o final do bloco de método ou primeira instrução de retorno, como qualquer outro método java.

A melhor maneira de resolver este problema é definir a página (onde você supõe que irá encaminhar a solicitação) dinamicamente de acordo com sua lógica. Isso é:

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 fazer o encaminhamento apenas uma vez na última linha ...

você também pode corrigir esse problema usando a instrução return após cada forward () ou colocar cada forward () no bloco if ... else


2

Eu removi

        super.service(req, res);

Então funcionou bem para mim


2

Colisão...

Eu apenas tive o mesmo erro. Percebi que estava invocando super.doPost(request, response);ao substituir o doPost()método, bem como invocando explicitamente o construtor da superclasse

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

Assim que comentei a declaração super.doPost(request, response);interna doPost()funcionou perfeitamente ...

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

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

}

Desnecessário dizer que preciso reler as super()práticas recomendadas: p


1

Você deve adicionar a instrução de retorno enquanto estiver encaminhando ou redirecionando o fluxo.

Exemplo:

se forwardind,

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

se redirecionando,

    response.sendRedirect(roundTripURI);
    return;

0

Após o método return forward, você pode simplesmente fazer isso:

return null;

Isso quebrará o escopo atual.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.