java.lang.IllegalStateException: Không thể (chuyển tiếp | sendRedirect | tạo phiên) sau khi phản hồi đã được cam kết


96

Phương pháp này ném

java.lang.IllegalStateException: Không thể chuyển tiếp sau khi phản hồi đã được cam kết

và tôi không thể phát hiện ra vấn đề. Bất kỳ giúp đỡ?

    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
Thật khó để nhìn thấy nó như thế này, nhưng có vẻ như bạn đã gửi một số đầu ra trước khi chuyển tiếp. Bạn có thể vui lòng in mã đầy đủ và kiểm tra xem bạn có bộ lọc nào không?
Kartoch

Câu trả lời:


244

Một hiểu lầm phổ biến giữa những người mới bắt đầu là họ nghĩ rằng lệnh gọi a forward(), sendRedirect()hoặc sendError()sẽ thoát ra và "nhảy" ra khỏi khối phương thức một cách kỳ diệu, do đó bỏ qua phần còn lại của mã. Ví dụ:

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

Điều này thực sự không đúng. Chúng chắc chắn không hoạt động khác với bất kỳ phương thức Java nào khác ( System#exit()tất nhiên là mong đợi ). Khi someConditionví dụ trên là truevà bạn đang gọi forward()theo sendRedirect()hoặc sendError()theo cùng một yêu cầu / phản hồi, thì cơ hội là rất lớn là bạn sẽ nhận được ngoại lệ:

java.lang.IllegalStateException: Không thể chuyển tiếp sau khi phản hồi đã được cam kết

Nếu ifcâu lệnh gọi a forward()và sau đó bạn gọi sendRedirect()hoặcsendError() , thì ngoại lệ bên dưới sẽ được đưa ra:

java.lang.IllegalStateException: Không thể gọi sendRedirect () sau khi phản hồi đã được cam kết

Để khắc phục điều này, bạn cần thêm một return;câu lệnh sau đó

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

... hoặc để giới thiệu một khối khác.

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

Để xác định nguyên nhân gốc rễ trong mã của bạn, chỉ cần tìm kiếm bất kỳ dòng nào gọi a forward(),sendRedirect() hoặc sendError()không phải thoát khỏi khối phương pháp hoặc bỏ qua phần còn lại của các mã. Điều này có thể nằm bên trong cùng một servlet trước dòng mã cụ thể, nhưng cũng có thể trong bất kỳ servlet hoặc bộ lọc nào đã được gọi trước servlet cụ thể.

Trong trường hợp sendError(), nếu mục đích duy nhất của bạn là đặt trạng thái phản hồi, hãy sử dụng setStatus()thay thế.


Một nguyên nhân có thể xảy ra khác là servlet ghi vào phản hồi trong khi một forward()sẽ được gọi, hoặc đã được gọi trong cùng một phương thức.

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

Kích thước bộ đệm phản hồi được mặc định trong hầu hết máy chủ là 2KB, vì vậy nếu bạn ghi nhiều hơn 2KB vào nó, thì nó sẽ được cam kết và forward() sẽ bị lỗi theo cách tương tự:

java.lang.IllegalStateException: Không thể chuyển tiếp sau khi phản hồi đã được cam kết

Giải pháp là rõ ràng, chỉ cần không ghi vào phản hồi trong servlet. Đó là trách nhiệm của JSP. Bạn chỉ cần đặt một thuộc tính yêu cầu như vậy request.setAttribute("data", "some string")và sau đó in nó trong JSP như vậy ${data}. Xem thêm trang wiki Servlets của chúng tôi để tìm hiểu cách sử dụng Servlets đúng cách.


Một nguyên nhân có thể xảy ra khác là servlet ghi tệp tải xuống vào phản hồi mà sau đó, ví dụ: a forward()được gọi.

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

Điều này là không thể về mặt kỹ thuật. Bạn cần xóa forward()cuộc gọi. Người dùng cuối sẽ ở trên trang hiện đang mở. Nếu bạn thực sự có ý định thay đổi trang sau khi tải tệp xuống, thì bạn cần chuyển logic tải tệp xuống tải trang của trang đích.


Tuy nhiên, một nguyên nhân có thể xảy ra khác là forward(), sendRedirect()hoặc sendError()các phương thức được gọi thông qua mã Java được nhúng trong tệp JSP theo cách cũ <% scriptlets %>, một phương pháp đã chính thức không được khuyến khích kể từ năm 2001 . Ví dụ:

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

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

Vấn đề ở đây là nội bộ JSP ngay lập tức viết văn bản mẫu (tức là mã HTML) qua out.write("<!DOCTYPE html> ... etc ...") ngay khi nó gặp phải. Do đó, về cơ bản đây là vấn đề giống như đã giải thích trong phần trước.

Giải pháp là rõ ràng, chỉ cần không viết mã Java trong tệp JSP. Đó là trách nhiệm của một lớp Java bình thường như Servlet hoặc Filter. Xem thêm trang wiki Servlets của chúng tôi để tìm hiểu cách sử dụng Servlets đúng cách.


Xem thêm:


Không liên quan đến vấn đề cụ thể của bạn, mã JDBC của bạn đang rò rỉ tài nguyên. Khắc phục điều đó là tốt. Để biết các gợi ý, hãy xem thêm Tần suất nên đóng Kết nối, Câu lệnh và Bộ kết quả trong JDBC?


2
Với một thời gian nghỉ ngơi, bạn có nghĩa là break;? Điều đó có nghĩa là mã đã nằm trong một số forhoặc whilevòng lặp trong đó forward()được gọi lặp lại trong vòng lặp (do đó không chính xác, bạn chỉ nên gọi chuyển tiếp một lần SAU KHI vòng lặp - hoặc để thoát khỏi vòng lặp vì nó dường như không cần thiết) .
BalusC

@BalusC Bạn có ý tưởng về vấn đề liên quan này không? stackoverflow.com/questions/18658021/…
confile

@confile: Tôi không thực hiện Grails, nhưng dựa trên ngăn xếp cuộc gọi, nó vẫn đang thực hiện forward()cuộc gọi trong khi lẽ ra không nên làm vậy. JSF, mà tôi quen thuộc, cũng làm điều đó trừ khi bạn gọi một cách rõ ràng FacesContext#responseComplete(). Câu hỏi có liên quan này (mà tôi thấy bằng cách sử dụng từ khóa "grails ngăn phản hồi hiển thị") có thể hữu ích: stackoverflow.com/questions/5708654/…
BalusC 14/09/13

@BalusC Grails về cơ bản là Java, nhưng vấn đề liên quan đến Servlet. Bạn có bất kỳ ý tưởng nào khác những gì tôi có thể làm. Tôi đặt trở lại sau mỗi lần hiển thị, chuyển hướng và chuyển tiếp như bạn đề xuất.
confile

@confile: Tôi biết. Tôi đã trả lời nguyên nhân: Grails vẫn đang thực hiện forward()cuộc gọi trong khi nó không nên thực hiện điều đó. Giải pháp là rõ ràng về mặt chức năng: yêu cầu nó không làm điều đó. Cụ thể là không có ý tưởng rằng bạn đã lập trình đảm nhận công việc Grails phải làm: xử lý phản hồi. Về mặt kỹ thuật, tôi không biết làm thế nào để nói với Grails điều đó. Nhưng tôi biết rằng rất nhiều khung công tác MVC khác hỗ trợ điều này (được hướng dẫn để không tự xử lý phản hồi), chẳng hạn như JSF, Spring MVC, Wicket, v.v. Tôi sẽ ngạc nhiên nếu điều này là không thể trong Grails.
BalusC 14/09/13

19

thậm chí thêm một câu lệnh trả về sẽ đưa ra ngoại lệ này, giải pháp duy nhất là mã này:

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

6

Thông thường, bạn thấy lỗi này sau khi bạn đã thực hiện chuyển hướng và sau đó cố gắng xuất thêm một số dữ liệu vào luồng đầu ra. Trong những trường hợp mà tôi đã thấy điều này trước đây, nó thường là một trong những bộ lọc đang cố gắng chuyển hướng trang, và sau đó vẫn chuyển tiếp đến servlet. Tôi không thể thấy bất kỳ điều gì sai ngay lập tức với servlet, vì vậy bạn có thể muốn thử xem xét bất kỳ bộ lọc nào mà bạn có.

Chỉnh sửa : Một số trợ giúp khác trong việc chẩn đoán sự cố…

Bước đầu tiên để chẩn đoán vấn đề này là xác định chính xác vị trí của ngoại lệ. Chúng tôi giả định rằng nó đang được ném bởi dòng

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

Nhưng bạn có thể thấy rằng nó đang được ném sau đó trong mã, nơi bạn đang cố gắng xuất ra luồng đầu ra sau khi bạn đã cố gắng thực hiện chuyển tiếp. Nếu nó đến từ dòng trên, thì có nghĩa là ở đâu đó trước dòng này bạn có:

  1. xuất dữ liệu ra luồng đầu ra, hoặc
  2. đã thực hiện chuyển hướng khác trước đó.

Chúc may mắn!


2

Điều này là do servlet của bạn đang cố gắng truy cập vào một đối tượng yêu cầu không còn tồn tại nữa .. Câu lệnh chuyển tiếp hoặc bao gồm của servlet không ngừng thực thi khối phương thức. Nó tiếp tục đến cuối khối phương thức hoặc câu lệnh trả về đầu tiên giống như bất kỳ phương thức java nào khác.

Cách tốt nhất để giải quyết vấn đề này chỉ cần đặt trang (nơi bạn cho là chuyển tiếp yêu cầu) động theo logic của bạn. Đó là:

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
}

và thực hiện chuyển tiếp chỉ một lần ở dòng cuối cùng ...

bạn cũng có thể khắc phục sự cố này bằng cách sử dụng câu lệnh return sau mỗi forward () hoặc đặt mỗi forward () trong khối if ... else


2

Tôi đã gỡ bỏ

        super.service(req, res);

Sau đó, nó hoạt động tốt cho tôi


2

Băng...

Tôi chỉ có cùng một lỗi. Tôi nhận thấy rằng tôi đang gọi super.doPost(request, response);khi ghi đè doPost()phương thức cũng như gọi hàm tạo lớp cha một cách rõ ràng

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

Ngay sau khi tôi nhận xét ra tuyên bố super.doPost(request, response);từ bên trong doPost()nó đã hoạt động hoàn hảo ...

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

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

}

Không cần phải nói, tôi cần đọc lại các super()phương pháp hay nhất: p


1

Bạn nên thêm lợi nhuận câu lệnh trong khi bạn đang chuyển tiếp hoặc chuyển hướng luồng.

Thí dụ:

nếu chuyển tiếp,

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

nếu chuyển hướng,

    response.sendRedirect(roundTripURI);
    return;

0

Sau khi quay lại phương thức chuyển tiếp, bạn có thể chỉ cần thực hiện điều này:

return null;

Nó sẽ phá vỡ phạm vi hiện tại.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.