Переменные пути в сопоставлении Spring WebSockets @SendTo

У меня есть, как мне кажется, очень простое приложение Spring WebSocket. Однако я пытаюсь использовать переменные пути для подписки, а также для сопоставления сообщений.

Я разместил перефразированный пример ниже. Я ожидаю, что аннотация @SendTo вернется к подписчикам на основе их fleetId. т.е. от POST до /fleet/MyFleet/driver/MyDriver должны уведомлять подписчиков /fleet/MyFleet, но я не вижу такого поведения.

Стоит отметить, что подписка на буквальное /fleet/{fleetId} работает. Это предназначено? Я пропустил какую-то часть конфигурации? Или это просто не так работает?

Я еще не очень хорошо знаком с WebSockets или этим проектом Spring, поэтому заранее спасибо.

Контроллер.java

...
@MessageMapping("/fleet/{fleetId}/driver/{driverId}")
@SendTo("/topic/fleet/{fleetId}")
public Simple simple(@DestinationVariable String fleetId, @DestinationVariable String driverId) {
    return new Simple(fleetId, driverId);
}
...

WebSocketConfig.java

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/live");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/fleet").withSockJS();
    }
}

index.html

var socket = new SockJS('/fleet');
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
    // Doesn't Work
    stompClient.subscribe('/topic/fleet/MyFleet', function(greeting) {
    // Works
    stompClient.subscribe('/topic/fleet/{fleetId}', function(greeting) {
        // Do some stuff
    });
});

Отправить образец

    stompClient.send("/live/fleet/MyFleet/driver/MyDriver", {}, JSON.stringify({
        // Some simple content
    }));

person bvulaj    schedule 20.11.2014    source источник


Ответы (3)


Несмотря на то, что @MessageMapping поддерживает заполнители, они не отображаются/не разрешаются в пунктах назначения @SendTo. В настоящее время невозможно определить динамические места назначения с помощью аннотации @SendTo (см. проблему SPR-12170). Вы можете использовать SimpMessagingTemplate на данный момент (в любом случае, так это работает внутри). Вот как вы это сделаете:

@MessageMapping("/fleet/{fleetId}/driver/{driverId}")
public void simple(@DestinationVariable String fleetId, @DestinationVariable String driverId) {
    simpMessagingTemplate.convertAndSend("/topic/fleet/" + fleetId, new Simple(fleetId, driverId));
}

В вашем коде пункт назначения '/topic/fleet/{fleetId}' обрабатывается как литерал, поэтому подписка на него работает, поскольку вы отправляете сообщения в тот же пункт назначения.

Если вы просто хотите отправить некоторые исходные пользовательские данные, вы можете вернуть их непосредственно в подписке:

@SubscribeMapping("/fleet/{fleetId}/driver/{driverId}")
public Simple simple(@DestinationVariable String fleetId, @DestinationVariable String driverId) {
    return new Simple(fleetId, driverId);
}

Обновление: в Spring 4.2 поддерживаются заполнители переменных назначения, теперь можно сделать что-то вроде:

@MessageMapping("/fleet/{fleetId}/driver/{driverId}")
@SendTo("/topic/fleet/{fleetId}")
public Simple simple(@DestinationVariable String fleetId, @DestinationVariable String driverId) {
    return new Simple(fleetId, driverId);
}
person Sergi Almar    schedule 21.11.2014
comment
Спасибо, @sergi! Я надеялся обойти с помощью MessagingTempalte, но это работает так же хорошо. В чем разница между использованием @SubscribeMapping и @SendTo? Первый просто пропускает брокера сообщений? - person bvulaj; 21.11.2014
comment
@SubscribeMapping перехватывает только подписки (а не сообщения на канал), возвращаемое значение будет отправлено непосредственно пользователю, но может быть переопределено с помощью @SendTo для отправки в другое место назначения (будет перенаправлено на BrokerChannel). @SendTo также можно использовать с @MessageMapping для отправки ответа определенному адресату. Таким образом, @SendTo можно использовать вместе с @MessageMapping и @SubscribeMapping. - person Sergi Almar; 21.11.2014
comment
я должен сделать тот же проект равным, но не могу начать, можно использовать тот же проект-прототип, например, в github. Я знаю, что работает Spring, но не работает WebSocket, мне нужно отправить сообщение двум людям, и сообщение такое же, как в Skype. один к одному, дерево, если пригласить больше одного пользователя - person Marcelo Ferreira; 15.12.2016
comment
Спасибо за ваши обновления в этом ответе, это было действительно полезно! - person Vladlen Gladis; 16.12.2016
comment
Привет @SergiAlmar, есть ли способ сделать то же самое, не используя топать? Мы рассматриваем возможность использования простых веб-сокетов для передачи потоковых данных в динамическую конечную точку, что-то вроде /path/{id} - person myspri; 09.08.2017
comment
@SergiAlmar Знаете ли вы какие-нибудь примеры проектов, в которых используются заполнители переменных? Не уверен в остальной части конфигурации для достижения этой цели. - person Jordan Mackie; 28.06.2018
comment
@JordanMackie проверьте github.com/salmar/spring-websocket-chat/blob/ - person Sergi Almar; 28.06.2018
comment
@myspri нет, если вы используете необработанный WebSocket, у вас не будет протокола приложения, поэтому вам придется его реализовать - person Sergi Almar; 28.06.2018
comment
@SergiAlmar строка @MessageMapping(/chat.private.{username}) мешает ли другим пользователям прослушивать эту конечную точку? то есть, если у меня есть имя пользователя jmack, я все еще могу получить доступ к /chat.private.someoneelse? Кроме того, я только что понял, что сегодня утром смотрел ваше выступление на веб-сокетах! Очень понравилось! - person Jordan Mackie; 28.06.2018
comment
@JordanMackie этот пункт назначения используется только для отправки личных сообщений, но не для их получения. Я использую пункт назначения пользователя (только для этого пользователя) для получения сообщений, пожалуйста, ознакомьтесь с документацией по пунктам назначения пользователя docs.spring.io/spring/docs/current/spring-framework-reference/ - person Sergi Almar; 28.06.2018
comment
@SergiAlmar Я прочитал их, моя текущая проблема заключается в попытке настроить что-то вроде групп многоадресной рассылки с веб-сокетами, где один пользователь публикует, а многие пользователи слушают. Все, что я могу найти в документах и ​​примерах, — это личное общение и трансляция один на один. - person Jordan Mackie; 28.06.2018
comment
@JordanMackie, вы можете передать сообщение и защитить пункт назначения с помощью Spring Security docs.spring.io/spring-security/site/docs/current/reference/html/. Если этого недостаточно, реализуйте перехватчик канала с собственной защитой. - person Sergi Almar; 28.06.2018

вы можете отправить переменную внутри пути. например, я отправляю "este/es/el/chat/java/" и получаю на сервере как "este:es:el:chat:java:"

клиент:

stompSession.send("/app/chat/este/es/el/chat/java/*", ...);

сервер:

@MessageMapping("/chat/**")
@SendToUser("/queue/reply")
public WebsocketData greeting(Message m,HelloMessage message,@Header("simpSessionId") String sessionId) throws Exception {
    Map<String, LinkedList<String>> nativeHeaders = (Map<String, LinkedList<String>>) m.getHeaders().get("nativeHeaders");
    String value= nativeHeaders.get("destination").getFirst().replaceAll("/app/chat/","").replaceAll("/",":");
person ivan rc    schedule 18.02.2019

На самом деле я думаю, что это то, что вы могли бы искать:

@Autorwired
lateinit var template: SimpMessageTemplate;

@MessageMapping("/class/{id}")
@Throws(Exception::class)
fun onOffer(@DestinationVariable("id") id: String?, @Payload msg: Message) {
    println("RECEIVED " + id)
    template.convertAndSend("/topic/class/$id", Message("The response"))
}

Надеюсь, это поможет кому-то! :)

person João Rodrigues    schedule 29.03.2020