验证码servlet导致java.lang.IllegalStateException异常:PWC3999:在响应已提交后无法创建会话

7
我正在使用SimpleCaptcha创建一个验证码输入,并验证了验证码输入。我使用以下代码创建了一个验证码输入。
HTML代码:
<form action="submit_proceed.do" method="post">
<img src="captchaImg" /><input type="text" name="captcha" value=""><br />
<input type="submit" value="Submit" name="submit" />
</form>

Java Servlet 代码:
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Iterator;
import nl.captcha.Captcha;

protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();

List errorMsgs = new LinkedList();
try{
    // Validate Captcha
    HttpSession session = request.getSession(true);
    String userCaptcha = request.getParameter("captcha");
    Captcha captcha = (Captcha) session.getAttribute(Captcha.NAME);
    if (!captcha.isCorrect(userCaptcha)) {
        errorMsgs.add("Please input the correct Captcha value.");
    }
} catch (RuntimeException e) {
    errorMsgs.add("An unexpected error: " + e.getMessage());
    RequestDispatcher view = request.getRequestDispatcher("/error.view");
    view.forward(request, response);
}

然而,我一直收到这个错误:
StandardWrapperValve[Captcha]: PWC1406: Servlet.service() for servlet Captcha threw exception
java.lang.IllegalStateException: PWC3999: Cannot create a session after the response has been committed

我如何在我的servlet上创建一个会话?我该如何解决这个问题?
非常感谢。

1
你从哪里获取session?你的方法是否缺少重要部分?HttpSession session = request.getSession(); 或类似语句在哪里?你如何获取session对象? - WildWezyr
@WildWezyr:我觉得在测试后我意外删除了这个内容,将其添加到代码中,HttpSession session = request.getSession(true); - jl.
6个回答

16

在响应已提交后无法创建会话

异常信息非常明确。这意味着处于非法状态。当响应已经提交时,您不能再设置/更改响应头。当响应的头部已经发送到客户端时,响应就已经被提交了。这是一个不可逆转的点。

无论何时输出流已被间接或直接刷新,响应都将被提交。这可能发生在向响应中写入超过2K时(但取决于服务器配置),或手动调用flush(),或进行了sendRedirect()调用。

每当需要创建会话时,服务器需要在响应头中设置一个cookie,以便可以识别特定的客户端并将其与服务器内存中的HttpSession实例关联起来。但如果响应已经提交,则不可能实现这一点,因此会出现此异常。

回到此问题的根本原因:

servlet Captcha 的 Servlet.service() 抛出异常

造成此问题的是具有servlet-nameCaptcha的servlet。您需要检查/调试整个请求-响应链,以查看已调用哪些servlet/filter以及其中哪些可能在Captcha servlet能够创建会话之前提交了响应。由于您的主题中缺少这些信息,我无法再为您提供更多帮助。

至少在给出的代码示例中,我看到您不必要地调用了response.getWriter()。我不确定现实世界中的代码是什么样子的,也许您已经剥离了一些行,但有可能您实际上正在向其写入内容,这可能是问题的根本原因。如果您写入的内容过多或对其进行了刷新,则响应将被提交。不要在 Servlet 中写入响应,因为它应该是一个控制器。通常使用 JSP 进行编写。或者如果是为了调试目的,请使用 stdout (System.out.println()) 或Logger


1
@BalusC:是的,你说得对。这确实是与会话有关的问题。而且验证码确实造成了问题。通过将会话setAttribute的位置移到生成验证码图像之前,成功解决了这个问题。谢谢。 - jl.

2

有问题的代码位于nl.captcha.servlet.SimpleCaptchaServlet Servlet中。如果您将其更改为StickyCaptcha,问题将会消失,但是以下是SimpleCaptcha Servlet中引起问题的行。

CaptchaServletUtil.writeImage(resp, captcha.getImage());
req.getSession().setAttribute(NAME, captcha);

实际上,该代码是将图像文件写入响应中(通常大于默认的2k)。

因此,目前您可以使用StickyCaptcha或者检查代码并修复该问题。


1

将这行移动:

HttpSession session = request.getSession(true);

这是doPost方法中的第一条语句。

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

  HttpSession session = request.getSession(true);

  response.setContentType("text/html;charset=UTF-8");

  //...
}

这应该有所帮助。


@WildWezyr:已经移到顶部,但异常信息仍然存在。 - jl.
@jl:你确定在测试之前已经正确地重新构建和部署了你的 Web 应用程序吗?有时候在测试应该有效的修复措施时,它们似乎并没有起作用,这是一个常见情况... - WildWezyr
@WildWezyr:我通过NetBeans重新构建、清理和加载。 - jl.
@jl:有时候 NetBeans 会出现一些卡顿,你需要停止并重新启动 Servlet 容器(如 Tomcat 或类似的东西),以便正确地重新加载已更改的 Web 应用程序... - WildWezyr

0

通过添加以下代码创建会话 javax.servlet.http.HttpSession session = request.getSession();


try{ 
 javax.servlet.http.HttpSession session = request.getSession();
    // Validate Captcha 
    String userCaptcha = request.getParameter("captcha"); 
    Captcha captcha = (Captcha) session.getAttribute(Captcha.NAME); 
    if (!captcha.isCorrect(userCaptcha)) { 
        errorMsgs.add("Please input the correct Captcha value."); 
    } 
} catch (RuntimeException e) { 
    ... 
} 

我们会期望session.getAttribute(Captcha.NAME)返回什么值? - djna

0

这一行

 (Captcha) session.getAttribute(Captcha.NAME);

这意味着在处理特定请求之前,会话应该已经存在,因此我想知道在发送原始表单之前是否有一些初始化工作需要完成。这应该由您正在使用的框架指定。

例如,您可能有一个初始的servlet

 creates the image, figures out the value of Capcha.Name
 creates the session 
 session.setAttribute(Capcha.NAME, theName)
 emit the html (or forward() to a JSP)

你可以在 JSP 中完成所有这些操作,或者你可以让你的 Servlet 转发到一个 JSP 页面。
有没有关于这个 catcha 库使用的示例可供查看?

@djna:在发送原始表单之前,我该如何进行初始化? - jl.
@djna:我正在使用Simple Captcha,http://simplecaptcha.sourceforge.net/index.html,但他们只提供了JSP的示例。我真的需要将其转换为Servlet。 - jl.

0
看起来,您在处理帖子之前已经向客户端发送了标头。如果您创建一个会话,则服务器需要将会话ID发送到客户端。通常通过发送cookie来实现这一点。我想您需要检查代码是否在将任何内容发送回客户端之前执行操作处理。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接