首页 > 解决方案 > HttpServletResponse.setStatus() 工作一次,再次调用时什么也不做 - 在 Java 8/Tomcat 9.0.0 和 Java 10/Tomcat 9.0.8 之间改变?

问题描述

我有一个在 Tomcat 9 下运行的 Java servlet,作为正常流程的一部分,它会调用HttpServletResponse#setStatus()几次。

在使用 Java 8(1.8.0u144,Tomcat 报告为 )的 Tomcat 9.0.0.M26 上运行时1.8.0_144-b01,它可以正常工作。

在 Tomcat 9.0.8.0 和 Java 10.0.1 上运行时(10.0.1+10Tomcat报告为改变了。但是,通过 HttpServletResponse#setHeader() 发送到客户端的其他标头似乎不受此影响;即使在 setStatus() 不再执行任何操作之后,setHeader() 也会成功添加标题。没有发送可能导致 HTTP 标头终止的中间输出数据。

这是一个最小的工作示例:

package org.example;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/HttpResponseStatusTestServlet")
public class HttpResponseStatusTestServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.getWriter().append("Testing ");
        response.setStatus(505);
        response.setStatus(506);
        response.getWriter()
            .append("Served at: ")
            .append(request.getContextPath())
            .append(" with: ")
            .append(Integer.toString(response.getStatus()));
    }
}

调用此 servlet 时,我希望返回字符串,因为在调用Testing Served at: ... with: 506之前设置的最后一个 HTTP 状态是. 返回给客户端的 HTTP 状态码也应该是.getStatus()506506

但是,我最终得到的是Testing Served at: ... with: 505一个505HTTP 状态。就像第二个 setStatus() 调用甚至不存在一样。

无论是否包含 setStatus() 调用之前的结果都是相同的(除了Testing输出开头的存在response.getWriter().append("Testing ");),因此它似乎与提前终止 HTTP 响应标头无关。

没有任何迹象表明我可以看到第二个 setStatus() 调用以任何方式失败,甚至它曾经存在过;似乎除了第一次调用 setStatus() 之外,在响应对象上调用 setStatus() 完全没有任何作用。

的返回值贯穿于有问题的服务器response.isCommitted()false的上述 servlet:getWriter().append("Testing ");调用后、setStatus(505)调用后和setStatus(506)调用后。

我意识到为同一个请求多次调用 setStatus() 可能有点不正统,但是:

使用通用的网络搜索引擎让我对正在发生的事情一无所知,而且我能够找到的文档并不表明 setStatus() 只能调用一次,也不能多次调用它。

标签: javatomcatservlets

解决方案


多次调用setStatus()是不被禁止的,如果你查看 Tomcat 内部,你会发现有些地方可以多次更改状态(当然,如果它被禁止,你会得到一个例外)。

这是由 Tomcat 9.0.10 和 9.0.9 中修复的回归错误引起的,但不是 9.0.8(没有查看错误的引入位置,可能在 9.0.8 中)。

如果它已经设置为超过 399 的值,则基本上尝试更改状态代码没有任何效果,因为

if (this.status > 399) {
    // Don't overwrite first recorded error status
    return;
}

推荐阅读