【问题标题】:Spring rest service with SseEmitter使用 SseEmitter 的 Spring 休息服务
【发布时间】:2016-02-19 11:39:33
【问题描述】:

当我调用服务器上的控制器时,我试图通知一个简单的 html 页面。我有一个调用我的控制器的 android 应用程序,完成后我想通知我的网页该控制器已被调用。

这是我的一些代码:

    @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;
    }
}

我看到的所有教程,控制器返回 SseEmitter 但我必须返回一个用户。我必须用另一个映射制作另一个控制器并监听那个 url 吗?我将如何在现有控制器中调用该控制器方法? 我的 EventSource 必须监听哪个 URL?

提前感谢您的帮助!

亲切的问候。

【问题讨论】:

    标签: java android spring rest server-sent-events


    【解决方案1】:

    我想你快到了,Allinone51。

    您对 SseEmitter.send() 的调用可能应该在 getUser 方法中。 一般模式是,当您创建 SseEmitter 时,您需要将其“存储”在某个地方以供其他代码获取。您正确地从 getSseEmitter 方法返回了 SseEmitter,您只是忘记存储它以便其他方法能够在其上调用“发送”。

    根据您上面的示例进行调整,可能是这样的:

    //...
    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;
    }
    

    当然,上面的代码只满足一个单一的连接/发射器。有更智能的方法来存储该发射器。例如,在我的在线游戏应用程序中,我将发射器连接到每个 Player 对象中。这样,每当我服务器上的播放器对象有东西要告诉播放器设备时,它就可以访问自身内部的正确发射器。

    【讨论】:

    • 您能否分享一下您如何存储 SseEmitter 的详细信息?我正在尝试将其存储在数据库中。
    • SseEmitter 是一个活动对象,所以我的意思不是存储在数据库中。你需要把它放在某个地方,例如在公共静态映射中,以便应用程序的其他部分可以获取它并使用它来发出消息。
    • @LeifJohn 我对代码 sn-p 有一个顾虑。如果客户端在无限循环中调用端点“/sse”,这可能会导致创建大量 SSEEmitter 对象。如果我错了,请纠正我。
    【解决方案2】:

    我设法使 SseEmitter 工作。我会解释我是怎么做到的,也许它会帮助别人。

    我发现的第一件事是 EventSource("url") 实际上只是“调用”该 url(如果我错了,请纠正我)。因此,在 Restcontroller 的情况下,它只会一遍又一遍地调用该控制器。现在,当您使用 SseEmitter.send 时,会触发 EventSource 的 onMessage() 方法。所以我所做的是添加另一个带有映射“/sse”的控制器方法,并让我的 eventSource 调用该方法。现在,当调用返回用户的原始控制器方法时,我只需将全局变量设置为 true,如果该变量为 true,则发送一条消息。代码会更好地解释这一点:

    我在我的 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;
        }
    }
    

    现在是带有 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>
    

    希望这对其他人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-13
      • 2022-01-08
      • 2016-04-02
      • 2017-05-26
      • 2016-05-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多