简体   繁体   中英

Spring rest service with SseEmitter

I'm trying to notify a simple html page when I call a controller on my server. I have an android application who calls my controller and when this is done I would like to notify my webpage that the controller was called.

Here is some of my code:

    @RequestMapping("/user") 
public class UserController {

    /**
     * Returns user by id.
     * 
     * @param user IMEI
     * @return
     */
    @RequestMapping(value = "/{imei}", method = RequestMethod.GET)
    public User getUser(@PathVariable String imei) {

        User myUser = null;
        try {
            myUser = DbConnector.getUserWithImei(imei);
        } catch (Exception e) {
            System.out.println("Couldn't get user from database");
            e.printStackTrace();
        }
        SseEmitter emitter = new SseEmitter();
        try {
            emitter.send("Hallokes");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        emitter.complete();
        return myUser;
    }
}

All tutorials I see, the controller returns SseEmitter but I have to return a User. Must I make another controller with another mapping and listen on that url? How would I call that controller method within my existing controller? To what URL must my EventSource listen?

Thanks in advance for your help!

Kind regards.

I think you are almost there, Allinone51.

Your call to SseEmitter.send() should probably be in the getUser method. The general pattern is that when you create the SseEmitter you need to "store" it somewhere for other code to get hold of it. You correctly return the SseEmitter from the getSseEmitter method, you just forgot to store it for the other method to be able to call 'send' on it.

Adjusting on your example above, it could be something like this:

//...
private SseEmitter emitter;

@RequestMapping(value = "/{imei}", method = RequestMethod.GET)
public User getUser(@PathVariable String imei) { 
    User myUser = null;

    // .. do resolving of myUser (e.g. database etc).

    // Send message to "connected" web page:
    if (emitter != null) {
        emitter.send(myUser.toString()); // Or format otherwise, e.g. JSON.
    }

    // This return value goes back as a response to your android device
    // i.e. the caller of the getUser rest service.
    return myUser;
}

@RequestMapping(value = "/sse")
public SseEmitter getSseEmitter() {
    emitter = new SseEmitter();
    return emitter;
}

Of course, the code above caters for only one single connection/emitter. There are more intelligent ways of storing that emitter. For example, in my online game application I hook the emitter into each Player object. That way whenever the player object on my server has something to tell to the player device, it has access to the correct emitter inside itself.

I managed to make the SseEmitter work. I'll explain how I did it, maybe it will help someone else.

The first thing I found out is that EventSource("url") actually just 'calls' that url (correct me if I'm wrong). So in the case of a Restcontroller it would just call that controller over and over. Now when you use SseEmitter.send, the onMessage() method of the EventSource get triggered. So what I did is add another controller method with mapping "/sse" and let my eventSource call that method. Now when my original controller method which returns a user is called, I just set a global variable to true, and if that variable is true, I send a message. Code will explain this better:

My method called within my android app follow by the controller method for SSE:

@RequestMapping(value = "/{imei}", method = RequestMethod.GET)
    public User getUser(@PathVariable String imei) { // @PathVariable for
                                                        // passing variables
                                                        // in uri.
                                                        // (<root-url>/service/greeting/myVariable)
        User myUser = null;
        try {
            myUser = DbConnector.getUserWithImei(imei);
        } catch (Exception e) {
            System.out.println("Couldn't get user from database");
            e.printStackTrace();
        }
        if (myUser != null) {
            if (myUser.getAccess() == 1) {
                calledImei = true;
            }
        }
        return myUser;
    }

    @RequestMapping(value = "/sse")
    public SseEmitter getSseEmitter() {
        SseEmitter sseEmitter = new SseEmitter();
        if(calledImei) {
            try {
                sseEmitter.send("Message 1");
                calledImei = false;
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        sseEmitter.complete();
        return sseEmitter;
    }
}

And now the HTML page with the EventSource:

<body>
    <script>
            var source = new EventSource("rest/user/sse");
            source.onmessage = function(event) {
                 //Do what you need to do when message received.
            }
    </script>
</body>

Hope this will help some other people.

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