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

Поширена помилка серед початківців є те , що вони думають , що поклик 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()буде викликано a , або його було викликано тим самим методом.

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

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

java.lang.IllegalStateException: Неможливо переслати після завершення відповіді

Рішення очевидне, просто не пишіть відповідь у сервлеті. Це відповідальність JSP. Ви просто встановлюєте атрибут запиту так, request.setAttribute("data", "some string")а потім друкуєте його у JSP так ${data}. Дивіться також нашу вікі-сторінку сервлетів, щоб дізнатися, як правильно використовувати сервлети.


Ще однією ймовірною причиною є те, що сервлет пише у відповідь файл для завантаження, після чого викликається, наприклад, a forward().

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?


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: Я знаю. Я вже відповів на причину: Grails все ще здійснює forward()дзвінок, хоча цього не повинен робити. Рішення функціонально очевидне: скажіть йому цього не робити. А саме, він не мав уявлення про те, що ви програмно взяли на себе роботу, яку повинен був виконувати Грейлс: обробляти відповідь. Технічно я навіть не уявляю, як це сказати Грейлсу. Але я знаю, що багато інших фреймворків MVC це підтримують (із вказівкою не обробляти відповідь самостійно), наприклад, JSF, Spring MVC, Wicket тощо. Буду здивований, якщо це неможливо в Grails.
BalusC

19

навіть додавання оператора return викликає цей виняток, для якого єдиним рішенням є цей код:

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

6

Зазвичай ви бачите цю помилку після того, як ви вже зробили переспрямування, а потім намагаєтесь вивести ще кілька даних у вихідний потік. У тих випадках, коли я бачив це в минулому, це часто один із фільтрів, який намагається перенаправити сторінку, а потім все одно переходить до сервлету. Я не бачу нічого негативного з сервлетом, тому ви можете спробувати поглянути на всі наявні у вас фільтри.

Редагувати : Додаткова допомога в діагностиці проблеми ...

Першим кроком до діагностики цієї проблеми є з’ясування, куди саме кидається виняток. Ми припускаємо, що його кидає лінія

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

Але ви можете виявити, що він викидається пізніше в коді, де ви намагаєтесь вивести у вихідний потік після того, як спробували зробити пересилання. Якщо він виходить із наведеного вище рядка, то це означає, що десь перед цим рядком у вас є:

  1. виведення даних у вихідний потік, або
  2. заздалегідь зробив чергове перенаправлення.

Удачі!


2

Це пов’язано з тим, що ваш сервлет намагається отримати доступ до об’єкта запиту, якого вже не існує. Оператор пересилання або включення сервлета не зупиняє виконання блоку методів. Він продовжується до кінця блоку методу або першого оператора повернення, як і будь-який інший метод 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.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()найкращі практики: с


1

Ви повинні додати оператор return під час переадресації або перенаправлення потоку.

Приклад:

якщо переслати,

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

при переспрямуванні,

    response.sendRedirect(roundTripURI);
    return;

0

Після методу повернення вперед ви можете просто зробити це:

return null;

Це порушить поточний обсяг.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.