Producing Server Sent Events in Spring Boot Webflux

Viewed 2420

I have the following code in a SpringBoot application:

class MySse {
    public Mono<ServerResponse> emitEvents(ServerRequest request){
        return ServerResponse.ok()
                .contentType(MediaType.TEXT_EVENT_STREAM)
                .body(Mono.from(Flux.interval(Duration.ofSeconds(1))
                .map(sequence  -> ServerSentEvent.builder()
                        .id(String.valueOf(sequence))
                        .event("periodic-event")
                        .data("SSE - " + LocalTime.now().toString())
                        .build())), ServerSentEvent.class);
    }
}

@Configuration
public class MyRoutesConfiguration {

    @Autowired
    @Bean
    RouterFunction<ServerResponse> sseRoute(MySse mySse) {
        return route(path("/sse").and(method(HttpMethod.GET)), MySse ::emitEvents)
                ;
    }

    @Bean
    public MySse mySse() {
        return new MySse();
    }
}

If I navigate to http://localhost (route not shown above but it works) from there I open DevTools in Chrome and type in the following JavaScript code:

const evtSource = new EventSource("sse/");
evtSource.onmessage = function(event) {
  console.log(event.data);
}

But nothing is printed out...

Breakpoints in the map(...) lambda in MySse::emitEvent are hit every second

But nothing is printed out in the browswer's JS console.

if I visit http://localhost/sse I get the following response:

id:0
event:periodic-event
data:SSE - 20:17:12.972706400
1 Answers
public Mono<ServerResponse> emitEvents(ServerRequest request){

    return ServerResponse.ok()
            .contentType(MediaType.TEXT_EVENT_STREAM)
            .body(BodyInserters.fromServerSentEvents(Flux.interval(Duration.ofSeconds(1))
            .map(aLong -> ServerSentEvent.<String>builder()
                            .id(String.valueOf(aLong))
                            .event("periodic-event")
                            .data("SSE - " + LocalTime.now().toString())
                            .build())));
}

if you want to stream the data you need to return a Flux. A Mono is ONE item, a Flux is 0...n items. You trigger the Flux to start emitting on each second interval and as long as the connection is open it will emit an event back to the calling client each second.

you can try it out with for example curl (or the chrome console as you are using) but you need to disable the curl buffering strategy using the -N flag if you are using curl.

curl -N http://localhost:8080/sse
Related