【问题标题】:java.lang.IllegalStateException: Cannot (forward | sendRedirect | create session) after response has been committedjava.lang.IllegalStateException:提交响应后无法(转发|发送重定向|创建会话)
【发布时间】:2020-11-19 06:55:43
【问题描述】:

这个方法抛出

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

【问题讨论】:

  • 很难看到这样,但似乎您在转发之前已经发送了一些输出。能否请您打印完整的代码并检查您是否没有任何过滤器?

标签: servlets response illegalstateexception forward requestdispatcher


【解决方案1】:

初学者之间的一个常见误解是,他们认为调用forward()sendRedirect()sendError() 会神奇地退出并“跳”出方法块,从而忽略代码的残余。例如:

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

因此这实际上是不正确的。它们的行为当然与任何其他 Java 方法没有什么不同(当然除了 System#exit())。当上面示例中的someConditiontrue 并且您在sendRedirect()sendError() 之后在相同的请求/响应中调用forward(),那么您的机会是会得到异常:

java.lang.IllegalStateException: 响应提交后无法转发

如果if 语句调用了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() 时写入响应,或者已在相同的方法中调用。

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

大多数服务器的响应缓冲区大小默认为 2KB,因此如果您向其写入超过 2KB,那么它将被提交,forward() 将以同样的方式失败:

java.lang.IllegalStateException: 响应提交后无法转发

解决方案很明显,只是不要在 servlet 中写入响应。这是 JSP 的职责。您只需像request.setAttribute("data", "some string") 那样设置一个请求属性,然后像${data} 那样在JSP 中打印它。另请参阅 our Servlets wiki page 了解如何正确使用 Servlet。


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

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

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


另一个可能的原因是forward()sendRedirect()sendError() 方法是通过嵌入在JSP 文件中的Java 代码以老式方式&lt;% scriptlets %&gt; 调用的,这种做法是officially discouraged since 2001。例如:

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

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

这里的问题是 JSP 内部一遇到模板文本(即 HTML 代码)就会立即通过out.write("&lt;!DOCTYPE html&gt; ... etc ...") 写入。因此,这与上一节中解释的问题基本相同。

解决方案很明显,只是不要在 JSP 文件中编写 Java 代码。这是普通 Java 类(如 Servlet 或 Filter)的职责。另请参阅 our Servlets wiki page 了解如何正确使用 Servlet。


另见:


与您的具体问题无关,您的 JDBC 代码正在泄漏资源。也解决这个问题。有关提示,另请参阅How often should Connection, Statement and ResultSet be closed in JDBC?

【讨论】:

  • 休息一下是指break;?这意味着代码在某个forwhile 循环中,其中forward() 在循环期间被重复调用(因此这是不正确的,你应该只在循环之后调用一次——或者摆脱循环,因为它显然是不需要的)。
  • @BalusC 你对这个相关问题有什么想法吗? stackoverflow.com/questions/18658021/…
  • @confile:我不使用 Grails,但根据调用堆栈,它仍在执行 forward() 调用,而它不应该这样做。我熟悉的 JSF 也会这样做,除非您明确调用 FacesContext#responseComplete()。这个相关问题(我发现使用关键字“grails prevent render response”)可能会有所帮助:stackoverflow.com/questions/5708654/…
  • @BalusC Grails 基本上是 Java,但问题与 Servlet 有关。你有什么其他想法我能做什么。按照您的建议,我在每次渲染、重定向和转发后都添加了一个 return。
  • @confile:我知道。我已经回答了原因:Grails 仍在执行forward() 调用,而它不应该这样做。解决方案在功能上很明显:告诉它不要这样做。它不知道您已经以编程方式接管了 Grails 应该做的工作:处理响应。从技术上讲,我不知道如何告诉 Grails。但我知道很多其他 MVC 框架都支持这一点(被指示不要自己处理响应),例如 JSF、Spring MVC、Wicket 等。如果这在 Grails 中是不可能的,我会感到惊讶。
【解决方案2】:

在返回转发方法之后,您可以简单地这样做:

return null;

它将打破当前范围。

【讨论】:

    【解决方案3】:

    撞……

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

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

    当我从 doPost() 语句中注释掉 super.doPost(request, response); 后,它就完美运行了......

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            //super.doPost(request, response);
            // More code here...
    
    }
    

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

    【讨论】:

      【解决方案4】:

      我删除了

              super.service(req, res);
      

      然后它对我来说很好

      【讨论】:

        【解决方案5】:

        这是因为您的 servlet 试图访问一个不再存在的请求对象。 servlet 的 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
        }
        

        在最后一行只进行一次转发...

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

        【讨论】:

          【解决方案6】:

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

          例子:

          如果转发,

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

          如果重定向,

              response.sendRedirect(roundTripURI);
              return;
          

          【讨论】:

            【解决方案7】:

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

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

            【讨论】:

              【解决方案8】:

              通常,您在完成重定向后会看到此错误,然后尝试将更多数据输出到输出流。在我过去看到过这种情况的情况下,它通常是试图重定向页面的过滤器之一,然后仍然转发到 servlet。我看不出这个 servlet 有什么问题,所以你可能想试试看一下你有什么过滤器。

              编辑:在诊断问题方面提供更多帮助……

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

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

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

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

              祝你好运!

              【讨论】:

                猜你喜欢
                • 2012-06-06
                • 1970-01-01
                • 2014-12-08
                • 1970-01-01
                • 2014-05-18
                • 2012-02-19
                • 2011-12-18
                相关资源
                最近更新 更多