简体   繁体   English

使用SseEmitter进行Spring休息服务

[英]Spring rest service with SseEmitter

I'm trying to notify a simple html page when I call a controller on my server. 当我在服务器上调用控制器时,我正在尝试通知一个简单的html页面。 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. 我有一个Android应用程序调用我的控制器,当这完成后,我想通知我的网页控制器被调用。

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. 我看到的所有教程,控制器返回SseEmitter但我必须返回一个User。 Must I make another controller with another mapping and listen on that url? 我必须使用另一个映射创建另一个控制器并侦听该URL吗? How would I call that controller method within my existing controller? 如何在现有控制器中调用该控制器方法? To what URL must my EventSource listen? 我的EventSource必须监听哪个URL?

Thanks in advance for your help! 在此先感谢您的帮助!

Kind regards. 亲切的问候。

I think you are almost there, Allinone51. 我想你几乎就在那里,Allinone51。

Your call to SseEmitter.send() should probably be in the getUser method. 您对SseEmitter.send()的调用可能应该在getUser方法中。 The general pattern is that when you create the SseEmitter you need to "store" it somewhere for other code to get hold of it. 一般模式是,当您创建SseEmitter时,您需要将其“存储”在某处以便其他代码获取它。 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. 您正确地从getSseEmitter方法返回SseEmitter,您只是忘记将其存储为另一种方法,以便能够在其上调用“send”。

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. 例如,在我的在线游戏应用程序中,我将发射器挂钩到每个Player对象中。 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. 我设法使SseEmitter工作。 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). 我发现的第一件事是EventSource(“url”)实际上只是“调用”那个url(如果我错了,请纠正我)。 So in the case of a Restcontroller it would just call that controller over and over. 因此,在Restcontroller的情况下,它只会一遍又一遍地调用该控制器。 Now when you use SseEmitter.send, the onMessage() method of the EventSource get triggered. 现在,当您使用SseEmitter.send时,会触发EventSource的onMessage()方法。 So what I did is add another controller method with mapping "/sse" and let my eventSource call that method. 所以我所做的是添加另一个带有映射“/ sse”的控制器方法,让我的eventSource调用该方法。 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. 现在当我调用返回用户的原始控制器方法时,我只是将一个全局变量设置为true,如果该变量为true,我发送一条消息。 Code will explain this better: 代码将更好地解释这个:

My method called within my android app follow by the controller method for SSE: 在我的Android应用程序中调用的方法遵循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: 现在是带有EventSource的HTML页面:

<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. 希望这会对其他人有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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