简体   繁体   English

Spring-boot 异步请求 - 响应主体为空

[英]Spring-boot asynchronous Request - Response body is empty

We have a Rest Controller that returns a WebAsyncTask.我们有一个返回 WebAsyncTask 的 Rest Controller。 The code is as follows:代码如下:

@ResponseBody
publicWebAsyncTask<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 is defined as follows: asyncTaskExecutor 定义如下:

<bean id="asyncTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<propertyname="corePoolSize" value="50"/>
<propertyname="maxPoolSize" value="50"/>
<propertyname="queueCapacity" value="5000"/>
</bean>

This is my Boot Application:这是我的启动应用程序:

@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();
}
}

I am using Postman to send the POST request and I get a 200 OK response, which is just an acknowledgement and not the actual response:我正在使用 Postman 发送 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

So, after a little investigation, I understood that I had to add DispatcherType ASYNC to my filter.因此,经过一些调查,我明白我必须将 DispatcherType ASYNC 添加到我的过滤器中。 This is my Filter:这是我的过滤器:

@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));
}
}

This is how I am registering the Filter in the TestBootApplication:这就是我在 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;
}

Now I get the actual response but I get an empty body.现在我得到了实际的回应,但我得到的是一个空的身体。 Also, the content-length is 0.此外,内容长度为 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

Here is an excerpt from the logs:以下是日志的摘录:

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]

I think the response is being dispatched before the controller finishes processing.我认为响应是在 controller 完成处理之前发送的。 I have invested a lot of time on this and haven't been able to find out the cause.我在这上面投入了很多时间,一直没能找出原因。 What am I missing here?我在这里错过了什么? I am using spring-boot version 2.6.12我正在使用 spring-boot 版本 2.6.12

You could try omitting the WebAsyncTask and diretly return the Callable from the controller method like this:您可以尝试省略WebAsyncTask并直接从 controller 方法返回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);
        };
    }

To configure your async task executor, you can create a global config bean like this:要配置异步任务执行器,您可以像这样创建一个全局配置 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);
    }

}

Side note: you can boost your chances of good answers if you format your code properly for SO.旁注:如果您为 SO 正确格式化代码,则可以增加获得良好答案的机会。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM