IllegalStateException:必须在 servlet 和所有过滤器上启用异步支持

IllegalStateException: async support must be enabled on a servlet and for all filters

我试图在控制器中使用 return 类型 Callable 以便在我处理请求时释放 servlet 线程,但是当 spring启动应用程序部署在开发环境中:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding \"true\" to servlet and filter declarations in web.xml.

注意:我已经阅读了几篇关于此错误的帖子,但其中 none 解决了我的问题。

我的特殊行为是本地一切都按预期工作:释放 servlet 线程并带外处理请求,最终 return 向客户端提供所需的响应。

但是当应用程序如前所述部署在开发环境中时,事情并不像预期的那样工作。

在本地测试时,我检查了 servlet/filters 是否支持异步;调试时要做的事情我只是在 ApplicationFilterChain 的给定过滤器中放置一个断点,然后检查整个链。在那里我可以检查 servlet 属性(在这里我看到 asyncSupported 为 true)以及链中包含的每个过滤器(我一个一个地检查它们;所有这些都将 asyncSupported 设置为 true)。

我还有一个从 AbstractAuthenticationProcessingFilter 扩展而来的自定义 JwtAuthenticationFilter 作为身份验证阶段的一部分,因此我还在此类过滤器中放置了一个断点并检查了链。我在那里看到了 originalChain(ApplicationFilterChain 之前评论过)和 "additionalFilters" 集合,其中出现了我的 JwtAuthenticationFilter 以及 spring 安全过滤器。但是 none 其中有 asyncSupported 属性 所以我假设它们不是开发服务器抛出的错误的一部分。请注意,正如我在本地提到的那样,一切正常;错误仅在部署到开发服务器时出现。

控制器方法:

  @GetMapping(path = "/export")
  public Callable<ResponseEntity> exportData() throws IOException {
    return new CustomContextAwareCallable(() -> handleResponse());
  }

所以我的问题是:即使 ApplicationFilterChain 上的每个过滤器和 servlet 都将 asyncSupported 设置为 true,为什么我在部署后会从服务器收到上面显示的错误?

应用程序没有部署为服务器上的 .WAR,它只是使用嵌入式 tomcat(我在本地做的一样)。

更新:另一个区别是在开发环境中,客户端请求通过 nginx 代理传递(只是想知道是否出于某种原因请求属性 org.apache.catalina.ASYNC_SUPPORTED 可以修改为 false(在本地它正在发生)。

有什么想法吗?

我通过向当前请求添加异步支持属性找到了解决方法,例如:

  @GetMapping(path = "/export")
  public Callable<ResponseEntity> exportData(HttpServletRequest request) throws IOException {
    request.setAttribute(org.apache.catalina.Globals.ASYNC_SUPPORTED_ATTR, true);
    return new CustomContextAwareCallable(() -> handleResponse());
  }

由于所有过滤器都与我正在使用的 dispatchServlet 一起被异步支持(正如在原始问题中评论的那样)似乎在开发环境中部署时,以下某些 tomcat 阀门正在设置此类属性请求为假:

  • 标准引擎阀门
  • 标准主机阀
  • 标准上下文阀

我验证了我在本地使用的嵌入式tomcat版本,它与开发环境相同:Apache Tomcat/8.5.23

似乎也是我之前显示的错误:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding \"true\" to servlet and filter declarations in web.xml.

并不像它描述的那样完全依赖于 servlet 和过滤器,因为列出的任何 tomcat 阀最终都可能不允许请求被异步支持。

目前我没有足够的细节来了解为什么会这样(如果有机会针对开发环境进行远程调试以查看每个 tomcat 阀门如何处理将是很好的请求)。

对于那些有 Filter 实施的人。您可以在 doFilter() 回调中对每个请求设置 request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, true),而不是在 Controller 中设置。这就是我在 Kotlin 中的做法:

import org.apache.catalina.Globals
import javax.servlet.Filter
import javax.servlet.FilterChain
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.annotation.WebFilter
import javax.servlet.http.HttpServletResponse

@Suppress("unused")
@WebFilter(urlPatterns = ["/v1/*"])
class RequestResponseFilter : Filter {

    override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
        request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, true)
        val httpServletResponse = response as HttpServletResponse
        httpServletResponse.setHeader("Cache-Control", "max-age=60")
        chain.doFilter(request, response)
    }

}