简体   繁体   中英

Spring Boot enable <async-supported> like in web.xml

I have a problem with Spring Boot configuration. I got exception on some mobile devices which should use sockets:

java.lang.IllegalArgumentException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml. Also you must use a Servlet 3.0+ container
    at org.springframework.util.Assert.isTrue(Assert.java:65)
    at org.springframework.http.server.ServletServerHttpAsyncRequestControl.<init>(ServletServerHttpAsyncRequestControl.java:59)
    at org.springframework.http.server.ServletServerHttpRequest.getAsyncRequestControl(ServletServerHttpRequest.java:202)
    at org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession.handleInitialRequest(AbstractHttpSockJsSession.java:202)
    at org.springframework.web.socket.sockjs.transport.handler.AbstractHttpSendingTransportHandler.handleRequestInternal(AbstractHttpSendingTransportHandler.java:66)
    at org.springframework.web.socket.sockjs.transport.handler.AbstractHttpSendingTransportHandler.handleRequest(AbstractHttpSendingTransportHandler.java:58)
    at org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService.handleTransportRequest(TransportHandlingSockJsService.java:254)
    at org.springframework.web.socket.sockjs.support.AbstractSockJsService.handleRequest(AbstractSockJsService.java:322)
    at org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler.handleRequest(SockJsHttpRequestHandler.java:88)
    at org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:51)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    ...

As I see from the exception, I have to enable async processing by adding true into the web.xml file. But this is the problem because I don't have it - our project uses Spring Boot.

Is there any way to provide the same functionality in Spring Boot like does?

You just need to define dispatcherServlet @Bean :

@Bean
public ServletRegistrationBean dispatcherServlet() {
    ServletRegistrationBean registration = new ServletRegistrationBean(
            new DispatcherServlet(), "/");
    registration.setAsyncSupported(true);
    return registration;
}

It overrides that default one from DispatcherServletAutoConfiguration .

Even though I am late to the party I am posting my working solution for posterity.

I had faced the same issue and tried the solution suggested by @Artem Bilan. But after debugging the code, I came to know that the servletRegistrationBean.isAsyncSupported() is by default true .

在此处输入图片说明 .

The actual error is being generated by the code block is from the method org.springframework.web.context.request.async.StandardServletAsyncWebRequest.startAsync() and we are getting this error when getRequest().isAsyncSupported() is false . Despite the ServletRegistrationBean#asyncSupported value is true , HttpServletRequest#isAsyncSupported() method value is always set to false for all API requests.

The Exerpt of the code block Code Reference

Assert.state(getRequest().isAsyncSupported(),
                "Async support must be enabled on a servlet and for all filters involved " +
                "in async request processing. This is done in Java code using the Servlet API " +
                "or by adding \"<async-supported>true</async-supported>\" to servlet and " +
                "filter declarations in web.xml.");

When analyzing further, I came to know that requestAttributes -> Globals.ASYNC_SUPPORTED_ATTR decides whether the request has to be processed in an Async way or not.

During the normal scenario, the requests do not have Globals.ASYNC_SUPPORTED_ATTR requestAttribute, hence request.asyncSupported will be false, if we manually add that requestAttribute ASYNC_SUPPORTED_ATTR value as true for all the requests using something like filters then request#asyncSupported value will be set to true as per the below code block Code Reference

specialAttributes.put(Globals.ASYNC_SUPPORTED_ATTR,
                new SpecialAttributeAdapter() {
                    @Override
                    public Object get(Request request, String name) {
                        return request.asyncSupported;
                    }

                    @Override
                    public void set(Request request, String name, Object value) {
                        Boolean oldValue = request.asyncSupported;
                        request.asyncSupported = (Boolean)value;
                        request.notifyAttributeAssigned(name, value, oldValue);
                    }
                });

Though there is no explicit answer, I have implemented a workaround for this issue. To Summarize all the required changes are listed below:

  1. Add a filter to set the request attribute as request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, true)

@Component
public class AsyncSupportFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, true);
        chain.doFilter(request, response);
    }
}

  1. Add filter to into your filter chain, in my case I have registered my filter into the spring context as below.

@Component
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
   @Autowired
   private AsyncSupportFilter asyncSupportFilter = null;
   @Override
   public void configure(HttpSecurity httpSecurity) {

       httpSecurity.addFilter(asyncSupportFilter);
       // we can add the filter before any filter like as httpSecurity.addFilterBefore(asyncSupportFilter , BasicAuthenticationFilter.class);
   }
}

Maybe it worth to mention that Spring Boot has async-support enabled by default for dispatcher servlet and all filters

Quotation from github :

We automatically set async-supported to true because the DispatcherServlet knows how to work with such requests and it's basically up to individual controller methods to return something like a DeferredResult. Furthermore, we also set async-supported for any filter (provided via getServletFilters) along with setting the DispatcherType for each filter to include ASYNC.

At least my practice supports this statement

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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