java.lang.IllegalStateException: ไม่สามารถ (ส่งต่อ | sendRedirect | สร้างเซสชัน) หลังจากที่มีการคอมมิตการตอบกลับ


98

วิธีนี้พ่น

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

คำตอบ:


249

ความเข้าใจผิดกันในหมู่เริ่มเป็นที่พวกเขาคิดว่าการเรียกร้องของที่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();
}

... หรือแนะนำบล็อกอื่น

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

เพื่อ naildown สาเหตุในรหัสของคุณเพียงค้นหาสำหรับสายการใด ๆ ที่เรียกว่าforward(), sendRedirect()หรือsendError()โดยไม่ออกจากบล็อกวิธีการหรือข้ามเศษเล็กเศษน้อยของรหัส สิ่งนี้สามารถอยู่ภายใน servlet เดียวกันก่อนบรรทัดโค้ดเฉพาะ แต่ยังอยู่ใน servlet หรือตัวกรองใด ๆ ที่ถูกเรียกก่อน servlet เฉพาะ

ในกรณีที่sendError()จุดประสงค์เดียวของคุณคือการตั้งค่าสถานะการตอบกลับให้ใช้setStatus()แทน


สาเหตุที่เป็นไปได้อีกประการหนึ่งคือ servlet เขียนไปยังการตอบสนองในขณะที่forward()จะเรียกหรือถูกเรียกด้วยวิธีการเดียวกัน

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

ค่าเริ่มต้นของขนาดบัฟเฟอร์การตอบกลับในเซิร์ฟเวอร์ส่วนใหญ่เป็น 2KB ดังนั้นหากคุณเขียนมากกว่า 2KB มันจะถูกผูกมัดและforward()จะล้มเหลวในลักษณะเดียวกัน:

java.lang.IllegalStateException: ไม่สามารถส่งต่อหลังจากมีการคอมมิตการตอบกลับ

วิธีแก้ปัญหานั้นชัดเจนอย่าเขียนตอบกลับใน servlet นั่นคือความรับผิดชอบของ JSP คุณเพียงแค่ตั้งค่าแอตทริบิวต์การร้องขอเช่นดังนั้นrequest.setAttribute("data", "some string")แล้วพิมพ์ใน JSP ${data}เช่นดังนั้น ดูหน้าวิกิ Servlets ของเราเพื่อเรียนรู้วิธีใช้ Servlets อย่างถูกวิธี


สาเหตุที่เป็นไปได้อีกประการหนึ่งคือ servlet เขียนการดาวน์โหลดไฟล์ไปยังการตอบสนองหลังจากนั้นเช่นforward()เรียก

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

นี่เป็นไปไม่ได้ในทางเทคนิค คุณต้องลบการforward()โทรออก enduser จะอยู่ในหน้าที่เปิดอยู่ หากคุณตั้งใจจะเปลี่ยนหน้าหลังจากดาวน์โหลดไฟล์แล้วคุณจะต้องย้ายตรรกะการดาวน์โหลดไฟล์ไปที่การโหลดหน้าของเพจเป้าหมาย


อีกสาเหตุน่าจะเป็นที่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 ปกติเช่น Servlet หรือ Filter ดูหน้าวิกิ Servlets ของเราเพื่อเรียนรู้วิธีใช้ Servlets อย่างถูกวิธี


ดูสิ่งนี้ด้วย:


ไม่เกี่ยวข้องกับปัญหาที่เป็นรูปธรรมของคุณรหัส JDBC ของคุณกำลังรั่วไหลทรัพยากร แก้ไขเช่นกัน สำหรับคำแนะนำโปรดดูที่Connection, Statement และ ResultSet ควรปิดบ่อยแค่ไหนใน JDBC?


2
ด้วยการหยุดพักคุณหมายถึงbreak;? นั่นหมายความว่ารหัสนั้นอยู่ภายในบางส่วนforหรือwhileวนซ้ำซึ่งมีการforward()เรียกซ้ำ ๆ ระหว่างลูป (ซึ่งไม่ถูกต้องคุณควรโทรไปข้างหน้าเพียงครั้งเดียวหลังจากลูป - หรือเพื่อกำจัดลูปเนื่องจากไม่จำเป็นต้องใช้) .
BalusC

@BalusC คุณมีความคิดเกี่ยวกับปัญหานี้หรือไม่? stackoverflow.com/questions/18658021/…
confile

@confile: ฉันไม่ได้ทำ Grails แต่ขึ้นอยู่กับ call stack มันยังคงทำการforward()โทรในขณะที่ไม่ควรทำเช่นนั้น JSF FacesContext#responseComplete()ซึ่งผมคุ้นเคยกับไม่ว่ายังเว้นแต่ว่าคุณได้โทร คำถามที่เกี่ยวข้องนี้ (ซึ่งฉันพบโดยใช้คำหลัก "grails ป้องกันการตอบสนองในการแสดงผล") อาจเป็นประโยชน์: stackoverflow.com/questions/5708654/…
BalusC

@BalusC Grails นั้นโดยพื้นฐานแล้ว Java แต่ปัญหาเกี่ยวข้องกับ Servlets คุณมีความคิดอื่น ๆ ที่ฉันสามารถทำได้ ฉันส่งผลตอบแทนหลังจากการแสดงผลแต่ละครั้งเปลี่ยนเส้นทางและส่งต่อตามที่คุณแนะนำ
เก็บตัว

@confile: ฉันรู้ ฉันได้ตอบสาเหตุแล้ว: Grails ยังคงทำการforward()โทรในขณะที่ไม่ควรทำเช่นนั้น วิธีแก้ปัญหานั้นชัดเจนในการทำงาน: บอกว่าอย่าทำอย่างนั้น ไม่มีความคิดเลยว่าคุณได้เข้ามาแทนที่งาน Grails โดยทางโปรแกรมควรจะทำ: การจัดการการตอบสนอง ในทางเทคนิคฉันไม่รู้ว่าจะบอก Grails อย่างไร แต่ฉันรู้ว่าเฟรมเวิร์ก MVC อื่น ๆ จำนวนมากสนับสนุนสิ่งนี้ (ได้รับคำสั่งให้ไม่จัดการการตอบสนองด้วยตัวเอง) เช่น JSF, Spring MVC, Wicket เป็นต้นฉันจะแปลกใจถ้าสิ่งนี้เป็นไปไม่ได้ใน Grails
BalusC

19

แม้แต่การเพิ่มคำสั่งส่งคืนจะทำให้เกิดข้อยกเว้นนี้ซึ่งมีเพียงวิธีเดียวคือรหัสนี้

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

6

โดยทั่วไปคุณจะเห็นข้อผิดพลาดนี้หลังจากที่คุณได้ทำการเปลี่ยนเส้นทางแล้วจากนั้นลองส่งข้อมูลเพิ่มเติมไปยังสตรีมเอาต์พุต ในกรณีที่ฉันเคยเห็นสิ่งนี้ในอดีตมักเป็นหนึ่งในตัวกรองที่พยายามเปลี่ยนเส้นทางหน้าจากนั้นยังส่งต่อไปยัง servlet ฉันไม่เห็นอะไรผิดปกติกับ servlet ในทันทีดังนั้นคุณอาจต้องการลองดูตัวกรองใด ๆ ที่คุณมีอยู่ด้วย

แก้ไข : ความช่วยเหลือเพิ่มเติมในการวินิจฉัยปัญหา ...

ขั้นตอนแรกในการวินิจฉัยปัญหานี้คือตรวจสอบให้แน่ใจว่าเกิดข้อยกเว้นตรงไหน เรากำลังสันนิษฐานว่าถูกโยนทิ้งโดยสาย

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

แต่คุณอาจพบว่ามีการส่งรหัสในภายหลังซึ่งคุณกำลังพยายามส่งออกไปยังสตรีมเอาต์พุตหลังจากที่คุณพยายามดำเนินการส่งต่อ ถ้ามันมาจากบรรทัดด้านบนแสดงว่าที่ไหนสักแห่งก่อนบรรทัดนี้คุณมี:

  1. เอาท์พุทข้อมูลไปยังสตรีมเอาต์พุตหรือ
  2. ทำการเปลี่ยนเส้นทางอื่นก่อนล่วงหน้า

โชคดี!


2

เนื่องจาก servlet ของคุณพยายามเข้าถึงอ็อบเจ็กต์การร้องขอซึ่งไม่มีอยู่อีกต่อไปแล้ว .. คำสั่ง forward หรือ include ของ servlet ไม่หยุดการดำเนินการบล็อกเมธอด มันยังคงดำเนินต่อไปจนถึงจุดสิ้นสุดของวิธีการบล็อกหรือคำสั่งส่งคืนแรกเช่นเดียวกับวิธีการอื่น ๆ ของ 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 block



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

คุณควรเพิ่มคำสั่งส่งคืนในขณะที่คุณกำลังส่งต่อหรือเปลี่ยนเส้นทางโฟลว์

ตัวอย่าง:

ถ้าส่งต่อ

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

หากเปลี่ยนเส้นทาง

    response.sendRedirect(roundTripURI);
    return;

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.