Как я могу отправить сообщение о событии подключения (SockJS, STOMP, Spring)?

Я подключаюсь через SockJS через STOMP к моему бэкэнду Spring. Все работает нормально, конфигурация работает для всех браузеров и т. Д. Однако я не могу найти способ отправить начальное сообщение. Сценарий будет следующим:

  1. Клиент подключается к теме

    function connect() {
        var socket = new SockJS('http://localhost:8080/myEndpoint');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function(frame) {
            setConnected(true);
            console.log('Connected: ' + frame);
            stompClient.subscribe('/topic/notify', function(message){
                showMessage(JSON.parse(message.body).content);
            });
        });
    }

а конфигурация бэкэнда выглядит примерно так:


    @Configuration
    @EnableWebSocketMessageBroker
    public class WebSocketAppConfig extends AbstractWebSocketMessageBrokerConfigurer {   
    ...
    @Override
    public void registerStompEndpoints(final StompEndpointRegistry registry) {
        registry.addEndpoint("/myEndpoint").withSockJS();
    }

  1. Я хочу отправить клиенту автоматический ответ от бэкэнда (на событие подключения), чтобы я уже мог предоставить ему некоторый набор данных (например, прочитать sth из базы данных) без необходимости для него (клиента) отправлять GET запрос (или любой другой). Подводя итог, я просто хочу отправить ему сообщение по теме с помощью объекта SimMessagingTemplate сразу после того, как он подключился.

Обычно я делаю это так, например в контроллере REST, когда шаблон уже настроен автоматически:


    @Autowired
    private SimpMessagingTemplate template;
    ...
    template.convertAndSend(TOPIC, new Message("it works!"));

Как этого добиться при подключении к событию?

ОБНОВЛЕНИЕ

Мне удалось заставить это работать. Однако меня все еще немного смущает конфигурация. Я покажу здесь 2 конфигурации, как можно отправить начальное сообщение:

1) Первое решение

Часть JS

stompClient.subscribe('/app/pending', function(message){
    showMessage(JSON.parse(message.body).content);
});
stompClient.subscribe('/topic/incoming', function(message){
    showMessage(JSON.parse(message.body).content);
});

Часть Java

@Controller
public class WebSocketBusController {
    @SubscribeMapping("/pending")

Конфигурация

@Override
public void configureMessageBroker(final MessageBrokerRegistry config) {
    config.enableSimpleBroker("/topic");
    config.setApplicationDestinationPrefixes("/app");
}

... и другие звонки

template.convertAndSend("/topic/incoming", outgoingMessage);

2) Второе решение

Часть JS

stompClient.subscribe('/topic/incoming', function(message){
    showMessage(JSON.parse(message.body).content);
})

Часть Java

@Controller
public class WebSocketBusController {
    @SubscribeMapping("/topic/incoming")

Конфигурация

@Override
public void configureMessageBroker(final MessageBrokerRegistry config) {
    config.enableSimpleBroker("/topic");
    // NO APPLICATION PREFIX HERE
}

... и другие звонки

template.convertAndSend("/topic/incoming", outgoingMessage);

РЕЗЮМЕ:

В первом случае используются две подписки - этого я хотел избежать и думал, что с этим можно справиться только с помощью одной.

У второго, однако, нет префикса для приложения. Но, по крайней мере, у меня может быть одна подписка, чтобы слушать предоставленную тему, а также отправлять начальное сообщение.


person Adam Soliński    schedule 15.07.2014    source источник


Ответы (2)


Если вы просто хотите отправить сообщение клиенту при подключении, используйте соответствующий ApplicationListener:

@Component
public class StompConnectedEvent implements ApplicationListener<SessionConnectedEvent> {

    private static final Logger log = Logger.getLogger(StompConnectedEvent.class);

    @Autowired
    private Controller controller;

    @Override
    public void onApplicationEvent(SessionConnectedEvent event) {
        log.debug("Client connected.");
        // you can use a controller to send your msg here
    }
}
person mrUwa    schedule 26.01.2017

Вы не можете сделать это на connect, однако @SubscribeMapping в этом случае делает то же самое.

Вам просто нужно пометить метод службы этой аннотацией, и он вернет результат функции subscribe.

Из справочного руководства Spring:

Аннотацию @SubscribeMapping также можно использовать для сопоставления запросов подписки с методами @Controller. Он поддерживается на уровне метода, но также может быть объединен с аннотацией @MessageMapping уровня типа, которая выражает общие сопоставления для всех методов обработки сообщений в одном контроллере.

По умолчанию возвращаемое значение из метода @SubscribeMapping отправляется в виде сообщения непосредственно обратно подключенному клиенту и не проходит через брокера. Это полезно для реализации взаимодействия сообщений запрос-ответ; например, для получения данных приложения при инициализации пользовательского интерфейса приложения. Или, в качестве альтернативы, метод @SubscribeMapping можно аннотировать с помощью @SendTo, и в этом случае результирующее сообщение отправляется в brokerChannel с использованием указанного целевого назначения.

ОБНОВЛЕНИЕ

Ссылаясь на этот пример: https://github.com/revelfire/spring4Test, как это можно было бы отправить что-нибудь при вызове строки 24 index.html: stamppClient.subscribe ('/ user / queue / socket / answers' ... из контроллеров Spring?

Ну вот так выглядят:

@SubscribeMapping("/queue/socket/responses")
public List<Employee> list() {
     return getEmployees();
}

Клиентская часть Stomp осталась прежней.

person Artem Bilan    schedule 15.07.2014
comment
Как я могу этого добиться? Учитывая приведенный выше код, я не могу добиться этого автоматически. Нужно ли мне выполнять дополнительный вызов (stpClient.send (/ app / myEndpoint / loadData, ...)) для получения исходного сообщения? Или есть какой-нибудь способ поймать это при подключении (именно первая функция js stamppClient.subscribe)? Я предполагаю, что это возможно, поскольку вы написали, что его можно вернуть в функцию подписки ... в любом случае, я не могу найти способ войти в это конкретное сопоставление. - person Adam Soliński; 15.07.2014
comment
Артем, а какой именно частью кода я должен поделиться? Насколько я понимаю, когда я создаю @Controller с помощью аннотированного метода @SubscribeMapping (/ topic / notify), я должен иметь возможность перехватить событие stamppClient.subscribe. Пожалуйста, дайте мне знать, какими элементами я должен поделиться, и я помещу их в текст вопроса выше. - person Adam Soliński; 15.07.2014
comment
Ссылаясь на этот пример: github.com/revelfire/spring4Test, как можно было бы отправить что-либо, когда вызывается строка 24 index.html: stpClient.subscribe ('/ user / queue / socket / answers' ... из контроллеров Spring? - person Adam Soliński; 15.07.2014
comment
К сожалению, это не работает. Я пробовал это раньше. Кстати, я использую Tomcat 7.0.54. - person Adam Soliński; 15.07.2014
comment
Мне удалось получить сообщение о подключении! Учитывая префикс / app в конфигурации websocket, когда я подписываюсь на app / test с SockJS и у меня есть @Controller с @SubscribeMapping (test), он входит в аннотированный метод! У меня сейчас нет доступа к коду, но завтра я обновлю ответ, чтобы он был разрешен. Спасибо, Артем, за уделенное время. BR, Адам - person Adam Soliński; 15.07.2014
comment
Тема теперь обновлена. Ответ на следующий вопрос теперь дает какое-то решение для себя. - person Adam Soliński; 16.07.2014