簡體   English   中英

Spring-boot 異步請求 - 響應主體為空

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

我們有一個返回 WebAsyncTask 的 Rest Controller。 代碼如下:

@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 定義如下:

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

我正在使用 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

因此,經過一些調查,我明白我必須將 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;
}

現在我得到了實際的回應,但我得到的是一個空的身體。 此外,內容長度為 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]

我認為響應是在 controller 完成處理之前發送的。 我在這上面投入了很多時間,一直沒能找出原因。 我在這里錯過了什么? 我正在使用 spring-boot 版本 2.6.12

您可以嘗試省略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);
        };
    }

要配置異步任務執行器,您可以像這樣創建一個全局配置 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 正確格式化代碼,則可以增加獲得良好答案的機會。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM