Spring WebSockets中@SendTo映射中的路径变量

61
我有一个非常简单的Spring WebSocket应用程序。但是,我尝试使用路径变量来订阅和消息映射。
下面是一个简化的示例。我期望@SendTo注释基于其fleetId返回给订阅者。即,对/fleet/MyFleet/driver/MyDriverPOST应该通知/fleet/MyFleet的订阅者,但我没有看到这种行为。
值得注意的是,订阅字面值/fleet/{fleetId}有效。这是有意的吗?我是否缺少某些配置?还是它就是这样工作的?
我对WebSockets或这个Spring项目不是很熟悉,提前感谢您。 Controller.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
    }));
3个回答

140

尽管@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);
}

谢谢@sergi!我希望能够避免使用MessagingTempalte,但那也同样奏效。使用@SubscribeMapping和@SendTo有什么区别?前者是否只是跳过了消息代理? - bvulaj
2
@SubscribeMapping 仅拦截订阅(而不是通道中的消息),返回值将直接发送给用户,但可以使用 @SendTo 覆盖以将其发送到另一个目标(将转发到 brokerChannel)。@SendTo 还可与 @MessageMapping 一起使用,将响应发送到特定目标。因此,基本上可以将 @SendTo@MessageMapping@SubscribeMapping 一起使用。 - Sergi Almar
3
感谢您对此答案的更新,它真的很有帮助! - Vladlen Gladis
你好@SergiAlmar,有没有一种不使用stomp就能实现相同功能的方法?我们正在考虑使用普通的Websockets来向动态端点提供流数据,例如/path/{id}。 - myspri
3
请查看 https://github.com/salmar/spring-websocket-chat/blob/c77467a77905abc24100d4e8040158c450d12a79/src/main/java/com/sergialmar/wschat/web/ChatController.java#L54。 - Sergi Almar
显示剩余7条评论

0

实际上我认为这可能是你正在寻找的内容:

@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"))
}

希望这能帮助到某个人! :)

0

你可以在路径中发送一个变量。例如,我发送了"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("/",":");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接