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 2010年

Answers:


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();
    }
}

要确定代码中的根本原因,只需搜索调用的任何行forward()sendRedirect()或者sendError()不退出方法块或跳过代码的剩余部分。它可以在特定代码行之前的同一servlet内,也可以在特定servlet之前被调用的任何servlet或过滤器中。

对于sendError(),如果您的唯一目的是设置响应状态,请setStatus()改用。


另一个可能的原因是,servlet forward()将在调用a 或已使用完全相同的方法调用时写入响应。

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 Wiki页面,以了解如何正确使用Servlet。


另一个可能的原因是,servlet将文件下载写入响应,然后forward()调用例如a 。

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

从技术上讲这是不可能的。您需要删除forward()呼叫。最终用户将停留在当前打开的页面上。如果您实际上打算在文件下载后更改页面,则需要将文件下载逻辑移至目标页面的页面加载。


另一个可能的原因是forward()sendRedirect()或者sendError()方法是通过嵌入在的老式方法形式的JSP文件的Java代码中调用<% scriptlets %>,这是一个实践从2001年开始正式气馁。例如:

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

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

这里的问题是,JSP会在内部立即通过out.write("<!DOCTYPE html> ... etc ...")它立即写入模板文本(即HTML代码)。因此,这基本上是与上一节中说明的问题相同的问题。

解决方案是显而易见的,只是不要在JSP文件中编写Java代码。这是普通Java类(例如Servlet或Filter)的职责。另请参阅我们的Servlets Wiki页面,以了解如何正确使用Servlet。


也可以看看:


您的具体问题无关,您的JDBC代码正在泄漏资源。修复它。有关提示,请参见在JDBC中应多久关闭一次Connection,Statement和ResultSet?


2
休息一下是break;什么意思?这将意味着该代码是被里面的一些forwhile环路,其中forward()在循环过程中反复调用(这是不正确。因此,你应该叫前一次的循环之后--or摆脱AS公司显然并不需要它的循环) 。
BalusC,2010年

@BalusC您对这个相关问题有想法吗?stackoverflow.com/questions/18658021/...
confile

@confile:我不执行Grails,但是基于调用堆栈,它仍在执行forward()调用,而不应执行该操作。我熟悉的JSF也会这样做,除非您显式调用FacesContext#responseComplete()。此相关的问题(我发现使用关键字“的Grails防止渲染响应”)可能会有所帮助:stackoverflow.com/questions/5708654/...
BalusC

@BalusC Grails基本上是Java,但是问题与Servlet有关。您还有其他想法吗?我按照您的建议在每个渲染之后放一个返回,重定向和转发。
2013年

@confile:我知道。我已经回答了原因:Grails仍在执行forward()呼叫,而不应该这样做。该解决方案在功能上很明显:告诉它不要这样做。也就是说,您根本不知道您已经以编程方式接管了Grails应该做的工作:处理响应。从技术上讲,我不知道如何告诉Grails。但是我知道很多其他MVC框架(例如,JSF,Spring MVC,Wicket等)也支持此功能(指示自己不处理响应)。如果在Grails中无法做到这一点,我会感到惊讶。
BalusC 2013年

19

即使添加return语句也会引发此异常,此代码的唯一解决方案是:

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

6

通常,在完成重定向后,您会看到此错误,然后尝试将更多数据输出到输出流。在过去我看到的情况下,通常是尝试重定向页面,然后仍转发到servlet的过滤器之一。我看不到servlet有什么立即出现的问题,因此您可能想尝试一下一下已有的任何过滤器。

编辑:诊断问题方面的更多帮助…

诊断此问题的第一步是确切确定引发异常的位置。我们假设它是由行抛出的

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

但是您可能会发现它在稍后的代码中被抛出,在您尝试进行转发之后,您将在此处尝试输出到输出流。如果它来自上面的行,则意味着在此行之前的某处您可以:

  1. 将数据输出到输出流,或
  2. 事先做了另一个重定向。

祝好运!


2

这是因为您的Servlet试图访问一个不再存在的请求对象。Servlet的forward或include语句不会停止方法块的执行。就像其他任何Java方法一样,它继续到方法块或第一个return语句的末尾。

解决此问题的最佳方法是根据逻辑动态设置页面(您打算在其中转发请求)。那是:

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
}

并且在最后一行只做一次前进...

您还可以在每个forward()之后使用return语句解决此问题,或将每个forward()放入if ... else块中



2

磕碰...

我只是有同样的错误。我注意到我super.doPost(request, response);在覆盖doPost()方法时正在调用以及显式在调用超类构造函数时在调用

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

当我注释掉super.doPost(request, response);from内在doPost()语句后,它就完美地发挥了作用。

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

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

}

不用说,我需要重新阅读super()最佳实践:p


1

在转发或重定向流时,应添加return语句。

例:

如果是forwardind,

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

如果重定向,

    response.sendRedirect(roundTripURI);
    return;

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.