我们有一个Rest控制器返回一个WebAsyncTask。代码如下:
@RequestMapping(value="/test", produces="text/plain", method=RequestMethod.POST)
@ResponseBody
public WebAsyncTask<ResponseEntity<String>> test(@RequestParam(value="foo", required=false) String data) throws IOException {
Callable<ResponseEntity<String>> callable=() -> {
HttpHeadersresponseHeaders = newHttpHeaders();
responseHeaders.setContentType(MediaType.TEXT_PLAIN);
return new ResponseEntity("testping",responseHeaders,HttpStatus.OK);
};
WebAsyncTask<ResponseEntity<String>> webAsyncTask = new WebAsyncTask<>(getTimeOut(), getAsyncTaskExecutor(), callable);
return webAsyncTask;
}
AsyncTaskExecutor的定义如下:
<bean id="asyncTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<propertyname="corePoolSize" value="50"/>
<propertyname="maxPoolSize" value="50"/>
<propertyname="queueCapacity" value="5000"/>
</bean>
这是我的启动程序:
@SpringBootApplication(exclude={JacksonAutoConfiguration.class,
CodecsAutoConfiguration.class,HttpMessageConvertersAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,WebMvcAutoConfiguration.class})
@ImportResource(locations={"classpath:META-INF/spring/test-boot-applicationContext.xml"})
@EnableWebSecurity
@EnableAsync
public class TestBootApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(TestBootApplication.class, args);
}
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurityhttpSecurity) throws Exception {
httpSecurity
.authorizeRequests().antMatchers("/**").permitAll()
.and().csrf().disable();
}
}
@Bean
public ServletRegistrationBean dispatcherController() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setNamespace("tempspace");
XmlWebApplicationContext applicationContext = new XmlWebApplicationContext();
applicationContext.setConfigLocations("classpath:META-INF/spring/test-dispatcher-servlet.xml");
dispatcherServlet.setApplicationContext(applicationContext);
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet,"/temp/*");
servletRegistrationBean.setName("testdispatcher");
servletRegistrationBean.setLoadOnStartup(20);
return servletRegistrationBean;
}
@Bean
public ObjectMapper defaultObjectMapper() {
return new ObjectMapper();
}
}
我使用邮差发送POST请求,我得到一个200 OK响应,这只是一个确认,而不是实际的响应:
KEY. VALUE
X-Frame-Options DENY
Content-Security-Policy frame-ancestors 'none'
X-Content-Type-Options nosniff
X-XSS-Protection 1; mode=block
Content-Length 0
Date Thu, 29 Dec 2022 09:33:38 GMT
Keep-Alive timeout=60
Connection keep-alive
因此,经过一番调查,我明白我必须将DispatcherType ASYNC添加到我的过滤器中。这是我的滤镜:
@Component
public class AsyncFilter implements Filter {
private static final Logger LOG = LoggerFactory.getLogger(AsyncFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
ContentCachingResponseWrapper responseWrapper = newContentCachingResponseWrapper((HttpServletResponse) response);
try {
filterChain.doFilter(requestWrapper,responseWrapper);
} finally {
LOG.trace("Before - ResponseWrapper: {}",
getResponsePayload(responseWrapper));
LOG.trace("Before - Response: {}",
getResponsePayload((HttpServletResponse) response));
byte[] responseArray = responseWrapper.getContentAsByteArray();
String responseString = new String(responseArray,
responseWrapper.getCharacterEncoding());
LOG.trace("####### responseString = {}", responseString);
responseWrapper.copyBodyToResponse();
LOG.trace("After - ResponseWrapper: {}",
getResponsePayload(responseWrapper));
LOG.trace("After - Response: {}",
getResponsePayload((HttpServletResponse) response));
}
}
这是我在TestBootApplication中注册过滤器的方式:
@Bean
public FilterRegistrationBean<AsyncFilter> asyncFilter() {
FilterRegistrationBean<AsyncFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new AsyncFilter());
registrationBean.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC));
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
现在我得到了实际的响应,但是得到了一个空体。另外,content-length为0。
KEY. VALUE
X-Frame-Options DENY
Content-Security-Policy frame-ancestors 'none'
X-Content-Type-Options nosniff
X-XSS-Protection 1; mode=block
Content-Type text/plain
Content-Length 0
Date Thu, 29 Dec 2022 09:33:38 GMT
Keep-Alive timeout=60
Connection keep-alive
以下是日志的摘录:
16:57:11.099 [http-nio-8081-exec-5] TRACE c.b.a.filters.AsyncFilter - Before - Response Wrapper: testping
16:57:11.100 [http-nio-8081-exec-5] TRACE c.b.a.filters.AsyncFilter - Before - Response: [unknown]
16:57:11.100 [http-nio-8081-exec-5] TRACE c.b.a.filters.AsyncFilter - ####### Response = testping
16:57:11.100 [http-nio-8081-exec-5] TRACE c.b.a.filters.AsyncFilter - After - Response Wrapper: [unknown]
16:57:11.100 [http-nio-8081-exec-5] TRACE c.b.a.filters.AsyncFilter - After - Response: [unknown]
我认为响应是在控制器完成处理之前被调度的。我在这个问题上花了很多时间,但还是没能找到原因。我遗漏了什么?我使用的是2.6.12版本的spring-boot
您可以尝试省略WebAsyncTask
并直接从控制器方法返回Callable
,如下所示:
public Callable<ResponseEntity<String>> test(@RequestParam(value = "foo", required = false) String data) {
return () -> {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.TEXT_PLAIN);
return new ResponseEntity("testping", responseHeaders, HttpStatus.OK);
};
}
要配置异步任务执行器,可以创建一个全局配置bean,如下所示:
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(10);
executor.setThreadNamePrefix("mvc-task-executor-");
executor.initialize();
configurer.setTaskExecutor(executor);
}
}
旁注:如果你正确地为SO格式化你的代码,你可以提高你的好答案的机会。