java.lang.IllegalStateException: невозможно (переадресовать | sendRedirect | создать сеанс) после того, как ответ был зафиксирован

96

Этот метод бросает

java.lang.IllegalStateException: невозможно пересылать после того, как ответ был зафиксирован

и я не могу определить проблему. Любая помощь?

    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
Это сложно увидеть, но похоже, что вы уже отправили какой-то вывод до своего пересылки. Не могли бы вы распечатать полный код и проверить, нет ли у вас фильтра?
Kartoch

Ответы:

244

Распространенное заблуждение среди начинающих состоит в том, что они думают, что вызов a forward(), sendRedirect()или sendError()мог бы волшебным образом выйти и «выпрыгнуть» из блока метода, таким образом игнорируя остаток кода. Например:

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

Таким образом, на самом деле это не так. Они определенно не ведут себя иначе, чем любые другие методы Java (за исключением, System#exit()конечно). Когда someConditionв приведенном выше примере trueвы вызываете forward()после sendRedirect()или sendError()по одному и тому же запросу / ответу, тогда велика вероятность того, что вы получите исключение:

java.lang.IllegalStateException: невозможно пересылать после того, как ответ был зафиксирован

Если ifоператор вызывает a, forward()а затем вы вызываете sendRedirect()или sendError(), будет выбрано следующее исключение:

java.lang.IllegalStateException: невозможно вызвать sendRedirect () после того, как ответ был зафиксирован

Чтобы исправить это, вам нужно либо добавить return;оператор позже

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

... или ввести блок else.

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

Чтобы выявить основную причину в вашем коде, просто найдите любую строку, которая вызывает a forward(), sendRedirect()или sendError()не выходя из блока метода или пропуская остаток кода. Это может быть внутри того же сервлета перед конкретной строкой кода, но также и в любом сервлете или фильтре, который был вызван перед определенным сервлетом.

В случае sendError(), если ваша единственная цель - установить статус ответа, используйте setStatus()вместо этого.


Другая вероятная причина заключается в том, что сервлет записывает в ответ, пока forward()будет вызван, или был вызван тем же методом.

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

Размер буфера ответа по умолчанию на большинстве серверов равен 2 КБ, поэтому, если вы напишете в него более 2 КБ, он будет зафиксирован и forward()завершится ошибкой таким же образом:

java.lang.IllegalStateException: невозможно пересылать после того, как ответ был зафиксирован

Решение очевидное, только не пишите в ответ в сервлете. Это ответственность JSP. Вы просто устанавливаете атрибут запроса таким образом, request.setAttribute("data", "some string")а затем распечатываете его в JSP вот так ${data}. См. Также нашу вики-страницу о сервлетах, чтобы узнать, как правильно использовать сервлеты.


Другой вероятной причиной является то, что сервлет записывает загрузку файла в ответ, после чего, например, forward()вызывается a .

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

Это технически невозможно. Вам нужно удалить forward()звонок. Конечный пользователь останется на текущей открытой странице. Если вы действительно намереваетесь изменить страницу после загрузки файла, вам необходимо переместить логику загрузки файла на загрузку целевой страницы.


Еще одна вероятная причина заключается в том forward(), что методы , sendRedirect()или sendError()вызываются через Java-код, встроенный в файл JSP, в форме старомодного способа <% scriptlets %>, практика, которая официально не поощрялась с 2001 года . Например:

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

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

Проблема здесь в том, что JSP внутренне немедленно записывает текст шаблона (то есть код HTML), out.write("<!DOCTYPE html> ... etc ...")как только он встречается. Таким образом, это, по сути, та же проблема, что объяснялась в предыдущем разделе.

Решение очевидно, просто не пишите Java-код в JSP-файле. Это ответственность обычного Java-класса, такого как сервлет или фильтр. См. Также нашу вики-страницу о сервлетах, чтобы узнать, как правильно использовать сервлеты.


Смотрите также:


Не связанный с вашей конкретной проблемой, ваш JDBC-код теряет ресурсы. Исправьте и это. Подсказки см. Также в разделе Как часто следует закрывать Connection, Statement и ResultSet в JDBC?

BalusC
источник
2
Вы имеете в виду с перерывом break;? Это означало бы, что код был внутри некоторого цикла forили, в whileкотором forward()он неоднократно вызывался во время цикла (что, таким образом, неверно, вы должны вызывать переадресацию только один раз ПОСЛЕ цикла - или чтобы избавиться от цикла, поскольку он, по-видимому, не нужен) .
BalusC
@BalusC У вас есть идеи по этой проблеме? stackoverflow.com/questions/18658021/…
confile
@confile: я не использую Grails, но на основе стека вызовов он все еще выполняет forward()вызов, хотя этого делать не должно. JSF, с которым я знаком, делает это также, если вы явно не вызываете FacesContext#responseComplete(). Этот связанный вопрос (который я нашел с помощью ключевых слов «grails предотвращает ответ на рендеринг») может быть полезен: stackoverflow.com/questions/5708654/…
BalusC
@BalusC Grails - это в основном Java, но проблема связана с сервлетами. У тебя есть еще идеи, что я могу сделать? Я ставлю возврат после каждого рендера, перенаправления и вперед, как вы предлагали.
confile
@confile: я знаю. Я уже ответил на причину: Grails все еще выполняет forward()вызов, хотя этого делать не должно. Решение функционально очевидно: скажите ему, чтобы он этого не делал. Он понятия не имел, что вы программно взяли на себя работу, которую должен был выполнять Grails: обработку ответа. Технически, я понятия не имею, как сказать об этом Grails. Но я знаю, что это поддерживают многие другие фреймворки MVC (которым даны инструкции не обрабатывать ответ сам по себе), например JSF, Spring MVC, Wicket и т. Д. Я был бы удивлен, если это невозможно в Grails.
BalusC
19

даже добавление оператора return вызывает это исключение, единственным решением для которого является следующий код:

if(!response.isCommitted())
// Place another redirection
user1503117
источник
6

Обычно эта ошибка появляется после того, как вы уже выполнили перенаправление, а затем пытаетесь вывести дополнительные данные в выходной поток. В тех случаях, когда я видел это в прошлом, часто один из фильтров пытается перенаправить страницу, а затем все еще перенаправляет ее на сервлет. Я не могу сразу увидеть что-то неправильное с сервлетом, поэтому вы можете попробовать взглянуть на любые фильтры, которые у вас есть.

Изменить : еще немного помощи в диагностике проблемы ...

Первым шагом к диагностике этой проблемы является точное определение места возникновения исключения. Мы предполагаем, что его выбрасывает линия

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

Но вы можете обнаружить, что он выбрасывается позже в коде, где вы пытаетесь вывести в выходной поток после того, как попытались выполнить пересылку. Если он исходит из вышеприведенной строки, это означает, что где-то перед этой строкой у вас есть:

  1. выводить данные в выходной поток, или
  2. заранее сделал еще один редирект.

Удачи!

Пол Вагланд
источник
2

Это связано с тем, что ваш сервлет пытается получить доступ к объекту запроса, которого больше не существует. Оператор forward или include сервлета не останавливает выполнение блока метода. Он продолжается до конца блока метода или первого оператора возврата, как и любой другой метод Java.

Лучший способ решить эту проблему - просто настроить страницу (на которую вы предполагаете перенаправить запрос) динамически в соответствии с вашей логикой. То есть:

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
}

и проделайте только один раз в последней строке ...

вы также можете решить эту проблему, используя оператор return после каждого forward () или помещая каждый forward () в блок if ... else

Суман Сенгупта
источник
2

Я удалил

        super.service(req, res);

Тогда это сработало для меня

kartikag01
источник
2

Удар...

У меня была такая же ошибка. Я заметил, что вызываю super.doPost(request, response);при переопределении doPost()метода, а также явно вызываю конструктор суперкласса

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

Как только я закомментировал заявление super.doPost(request, response);изнутри, doPost()оно сработало отлично ...

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

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

}

Излишне говорить, что мне нужно перечитать super()передовой опыт: p

Джон Рэмбо
источник
1

Вы должны добавить оператор return во время пересылки или перенаправления потока.

Пример:

если переадресация,

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

при перенаправлении,

    response.sendRedirect(roundTripURI);
    return;
Ашиш Мишра
источник
0

После метода возврата вперед вы можете просто сделать это:

return null;

Это нарушит текущий объем.

Амир Амири
источник