简体   繁体   中英

Grails Server Sent Event

I need to get Server-sent-events working with Grails. I feel like I am close, but just not quite there. The JavaScript request successfully reaches the controller, but it throws an error every time. It keeps retrying once every 2 seconds or so (probably due to the error).

I need to send an event to the user when the server's session timer gets below 5 minutes. I am trying to use HTML5's EventSource because I only want to send a single request to the server (multiple requests will reset the session timer each time). According to Lean Java Engineering , HTML5 meets my need.

- Javascript -

console.log("Starting eventSource");
var eventSource = new EventSource("SSETest/push");
console.log("Started eventSource");
eventSource.onmessage   = function(event) { console.log("Message received: " + event.data); };
eventSource.onopen      = function(event) { console.log("Open " + event); };
eventSource.onerror     = function(event) { console.log("Error " + event); };
console.log("eventState: " + eventSource.readyState);
// Stop trying after 10 seconds of errors
setTimeout(function() {eventSource.close();}, 10000);

- Grails Controller -

package sessionManager
class SSETestController {
    def push = {
        println "$request made it!"
        response.setContentType("text/event-stream, charset=UTF-8")
        response << "data: the data"
        render "HI"
    }
}

- Grails Console -

org.apache.catalina.core.ApplicationHttpRequest@4f889fad made it!
org.apache.catalina.core.ApplicationHttpRequest@13f78b8c made it!
org.apache.catalina.core.ApplicationHttpRequest@4b50734c made it!
org.apache.catalina.core.ApplicationHttpRequest@4c4bde24 made it!

- JavaScript Console -

Starting eventSource
Started eventSource
eventState: 0
Open [object Event] 
Error [object Event] 
Open [object Event] 
Error [object Event]
Open [object Event] 
Error [object Event]
Open [object Event]
Error [object Event]

Thanks in advance, Chris Hancock

In Grails controller, you need to change to something like this :

package sessionManager
class SSETestController {
    def push = {
        println "$request made it!"
        response.setContentType("text/event-stream, charset=UTF-8")
        //response << "data: the data\n\n"
        render "data: the data\n\n"
    }
}

In its basic form, the response should contain a "data:" line, followed by your message, followed by two "\\n" characters to end the stream:

data: My message\n\n

Now the response in console is :

Starting eventSource
Started eventSource
eventState: 0
Open [object Event]
Message received: the data
Error [object Event]
Open [object Event]
Message received: the data
Error [object Event]
Open [object Event]
Message received: the data
Error [object Event]
Open [object Event]
Message received: the data
Error [object Event]

For more details you can look on http://www.html5rocks.com/en/tutorials/eventsource/basics/ .

UPDATE

Why does it throw an error every time, though?

There can be different reason for error for example : network timeout. In our case, it is due to connection being closed between server and browser.

Why does it continually ping the server?

The connection between server and browser is closed after browser gets the data . So the browser tries to reconnect roughly 3 seconds after each connection is closed.

I thought HTML5 was supposed to connect once when the JavaScript ran the first time, and receive events as long as the controller sent them.

It is supposed to have always connection with server but not connect once.
Yes, it receives events as long as the controller sent them message. But in our case the server/controller just send data: the data\\n\\n and connection is closed.
In order to receive message continuously, you need to have loop in the controller action. For example :

package sessionManager
class SSETestController {
    def push = {
        println "$request made it!"
        response.setContentType("text/event-stream, charset=UTF-8")
        for(int i = 0 ; i < 10 ; i++){
            render "data: the data\n\n"
            Thread.sleep(1000)
        }
        render "data: finished\n\n"
    }
}

You can also use while(true) loop.

If you want to more about onerror , you can see this link http://www.htmlgoodies.com/beyond/reference/receive-updates-from-the-server-using-the-eventsource.html .
According to above link,

There is an onerror() event handler, but it's not as useful as you might think. It can however tell us some things about what's happening, thanks to the EventSource's readyState property. For instance, a value of EventSource.CONNECTING means that the connection was lost and the EventSource is attempting to reconnect. If something goes very wrong, a value of EventSource.CLOSED will let you know. Other than that, you can't get much information as there is no error object to query. Instead, it is the event object itself that is passed to the handler. The EventSource may be accessed via the eventSource or eventTarget properties, since both point to the same object.

Note that in Grails 3.2 there is native support for Server Sent Events via the RxJava plugin .

An example application is available too, as well as a guide on the grails guides page .

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