我正在尝试在控制器中使用返回类型Callable以便在处理请求时释放servlet线程,但是当在开发环境中部署spring boot应用程序时,我收到以下错误:
org.springframework.web.util.NestedServletException:请求处理失败;嵌套异常是java.lang.IllegalStateException:必须在servlet和异步请求处理中涉及的所有过滤器上启用异步支持。这是使用Servlet API在Java代码中完成的,或者通过在web.xml中向servlet和过滤器声明添加\“true \”来完成。
注意:我已经阅读了几篇关于此错误的帖子,但没有一个解决了我的问题。
我所拥有的特定行为是本地一切都按预期工作:释放servlet线程并在带外处理请求以最终返回到客户端所需的响应。
但是,如前所述,当应用程序部署在开发环境中时,事情就无法按预期工作。
在本地测试时,我检查了servlet / filters是否支持异步;调试时要做的只是在ApplicationFilterChain
的给定过滤器中放置一个断点,然后检查整个链。在那里,我可以检查servlet属性(我在其中看到asyncSupported on true)以及链中包含的每个过滤器(我逐个检查它们;所有这些都设置为asyncSupported为true)。
我还有一个自定义JwtAuthenticationFilter
,它从AbstractAuthenticationProcessingFilter
扩展,作为身份验证阶段的一部分,所以我也在这样的过滤器中放置一个断点并检查链。在那里,我看到原始链接(之前评论过的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
可能被修改为假(本地它是真的)。
有任何想法吗?
我通过向当前请求添加async支持属性找到了一种解决方法,如:
@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和异步请求处理中涉及的所有过滤器上启用异步支持。这是使用Servlet API在Java代码中完成的,或者通过在web.xml中向servlet和过滤器声明添加\“true \”来完成。
并不像它描述的那样专门绑定到servlet和过滤器,因为任何列出的tomcat阀门最终都可能不允许异步支持请求。
目前我没有足够的细节知道为什么会发生这种情况(有可能在开发环境中远程调试这个以查看每个tomcat阀门如何处理请求)。
对于那些有Filter
实现的人。您可以在request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, true)
回调中执行相同操作,而不是将Controller
设置为doFilter()
中的每个请求。这就是我在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)
}
}