【问题标题】:IllegalStateException: async support must be enabled on a servlet and for all filtersIllegalStateException:必须在 servlet 和所有过滤器上启用异步支持
【发布时间】:2018-10-01 19:24:48
【问题描述】:

我试图在控制器中使用返回类型 Callable 以便在处理请求时释放 servlet 线程,但是在开发环境中部署 Spring Boot 应用程序时出现以下错误:

org.springframework.web.util.NestedServletException: 请求 处理失败;嵌套异常是 java.lang.IllegalStateException:必须在 servlet 和异步请求处理中涉及的所有过滤器。这 在 Java 代码中使用 Servlet API 或通过添加 \"true\" 到 servlet 和过滤器 web.xml 中的声明。

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

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

但是当应用程序如前所述部署在开发环境中时,事情并没有按预期工作。

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

我还有一个自定义的JwtAuthenticationFilter,它从AbstractAuthenticationProcessingFilter 扩展为身份验证阶段的一部分,因此我还在此类过滤器中放置了一个断点并检查链。在那里我看到了 originalChain(之前评论过的ApplicationFilterChain)和“additionalFilters”集合,其中出现了我的JwtAuthenticationFilter 以及弹簧安全过滤器。但是它们都没有 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 可以修改为假(在本地它是真的)。​​

有什么想法吗?

【问题讨论】:

  • @UsamaAmjad 是的,我刚刚记录了请求期间 ApplicationFilterChain 上存在的所有过滤器,并且所有过滤器都支持异步(可以通过使用 ((ApplicationFilterChain)chain).findNonAsyncFilters(nonAsyncFilters);在组成链的任何过滤器中的 doFilter 方法中)。因此,由于所有过滤器及其 serlvet 都支持异步;我怀疑将tomcat设置请求属性org.apache.catalina.ASYNC_SUPPORTED设置为false(但仍然不知道为什么)。

标签: spring spring-mvc servlet-filters callable


【解决方案1】:

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

  @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 阀门将请求的此类属性设置为 false:

  • 标准发动机阀
  • 标准主机阀
  • 标准上下文阀

我验证了我在本地使用的嵌入式tomcat版本,和开发环境一样:Apache Tomcat/8.5.23

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

org.springframework.web.util.NestedServletException: 请求 处理失败;嵌套异常是 java.lang.IllegalStateException:必须在 servlet 和异步请求处理中涉及的所有过滤器。这 在 Java 代码中使用 Servlet API 或通过添加 \"true\" 来完成 web.xml 中的 servlet 和过滤器声明。

并不像它所描述的那样专门与 servlet 和过滤器相关联,因为任何列出的 tomcat 阀门最终都可能不允许异步支持请求。

目前我没有足够的细节来知道为什么会发生这种情况(如果有机会针对开发环境进行远程调试以查看每个 tomcat 阀门如何处理请求,那就太好了)。

【讨论】:

    【解决方案2】:

    对于那些拥有Filter 实现的人。您可以在doFilter() 回调中执行相同的操作,而不是为您的Controller 中的每个请求设置request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, true)。这就是我在 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)
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 2012-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-19
      • 1970-01-01
      • 2015-12-19
      • 1970-01-01
      相关资源
      最近更新 更多