HandlerInterceptor Interception SseEmitter Mapping Multiple execution preHandle

AsyncContextImpl AsyncContextImpl.complete is not called

Comment From: FPEsocrter

AsyncContextImpl AsyncContextImpl.complete is not called

Comment From: snicoll

@FPEsocrter sorry but the description is not actionable. I am not sure if you're trying to report a bug or a request for enhancement. Can you please edit the original description and provide more details?

Comment From: FPEsocrter

@Component
public class ComputingPowerCheckerInterceptor implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

        System.out.println("preHandle");
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

        System.out.println("afterCompletion");
    }
}
package cn.biz;

import jakarta.servlet.AsyncContext;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;

import jakarta.servlet.http.HttpServletRequest;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.apache.coyote.ActionCode;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.Map;


@RestController
@RequestMapping("/language")
public class LanguageController {

    @GetMapping("/open")
    public Map<String, Object> open() {
        return Map.of("", "");
    }

    @GetMapping("/see")
    public SseEmitter See(HttpServletRequest request ) throws Exception {
        System.out.println("see");
        SseEmitter sseEmitter = new SseEmitter();

        new Thread(() -> {

            try {
                sseEmitter.send("tes", MediaType.TEXT_PLAIN);
                Thread.sleep(3*1000);
                sseEmitter.send("tes", MediaType.TEXT_PLAIN);
                sseEmitter.complete();

            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).start();
        return sseEmitter;
    }

    @GetMapping("/response")
    public void testResp(ServletRequest request, ServletResponse response){
        AsyncContext asyncContext = request.startAsync();
        System.out.println("response");
        asyncContext.start(() -> {
            try {
                PrintWriter out = asyncContext.getResponse().getWriter();
                out.println("test");
                out.flush();
                Thread.sleep(3*1000);
                out.println("test");
                out.flush();
                out.close();
                asyncContext.complete();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });

    }
}

transfer /response preHandle one response Print Order preHandle response
afterCompletion

transfer / see preHandle twice
see Print Order preHandle see
afterCompletion preHandle

Normally it should only be called once preHandle

Comment From: FPEsocrter

sseEmitter.complete(); AsyncContextImpl.complete is not called. is bug

Comment From: snicoll

Sorry but dumping code in text like that is not very helpful. Someone might see what the problem could be, but I don't. If you want to speed up support, please move all that code into an actual small application we can run ourselves. You can attach a zip to this issue or push the code to a GitHub repository.

Comment From: rstoyanchev

Generally Spring MVC executes async requests in two phases as described in the docs. The first covers controller method invocation and async handling. After that we perform an ASYNC dispatch to complete processing on a Servlet container thread, which is required in some cases like view rendering.

The same is true for the SseEmitter scenario as well, in which case it looks a bit more strange because there is no further handling once the stream is over, but it provides consistency in the Spring MVC request lifecycle (exception handling, interception, etc).

If you want to ignore the second preHandle you can check if request.getDispatcherType() is ASYNC.