如何使用Spring WebSocket向自定义用户发送自定义消息?
我是 spring websocket .我想将产品变更发送给客户.为此,我想这样做: 客户端创建套接字连接并订阅目标:
I'm new to spring websocket. I want to send product changes to clients. For this, I want to do it as follows: Client creates a socket connection and subscribes destination:
var socket = new SockJS('/websocket');
var stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
stompClient.subscribe('/product/changes', function (scoredata) {
// We received product changes
});
});
//Send Ajax request and say server I want to know product with id=5 changes.
sendAjaxRequest(5);
我已将spring应用程序配置如下:
I've configured spring app as follow:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/product/");
registry.setApplicationDestinationPrefixes("/app");
}
}
现在我需要以下方法:
@RestController
public class ProductController {
@GetMapping("product-{id}")
public void startSubscribe(@PathVariable("id") Long id) {
// register current websocket session with product id and
// then with convertAndSendToUser send changes to current user.
}
}
我该如何实施?
首先,我的问题是,当您成功将Websocket与stomp集成时,为什么要尝试向rest控制器发送http请求? 如果我正确地理解了您的用例,那么应该可以想到的三个atm解决方案.
My question in the first place would be, why are you trying to send a http request to a rest controller when you successfully integrated websockets with stomp? If I am understanding your use case correctly, there should be three solutions I can think of atm.
您可以通过打开的websocket连接将请求直接从客户端发送到服务器.然后,Spring可以确定哪个Websocket会话进行了调用,您可以实现您的业务逻辑.您需要激活另一个称为"/queue"的代理,并为订阅不打算用于广播时所需的用户目标指定前缀.在客户端,您还必须更改您的订阅路径.最后,您必须创建一个用@Controller注释的类,其中包含您的消息映射,以从连接的客户端接收消息.
You can send your request directly from your client to the server via the open websocket connection. Spring can then determine which Websocket session made the call and you can implement your business logic. You need to activate another broker called "/queue" and specify the prefix for the user target that is needed when a subscription is not intended for a broadcast. On the client side, you must also change your subscription path. Finally, you must create a class that is commented with @Controller that contains your message mappings to receive messages from the connected client.
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue", "/product"); // <- added "/queue"
registry.setApplicationDestinationPrefixes("/app");
registry.setUserDestinationPrefix("/user");
}
}
服务器控制器
@Controller
public class WebSocketContoller{
@Autowired
private SimpMessagingTemplate simpMessagingTemplate;
@MessageMapping("/product/register")
public void register(@Payload Long productId, @Header("simpSessionId") String sessionId) {
// register current websocket session with product id and
// then with convertAndSendToUser send changes to current user.
// Example of how to send a message to the user using the sessionId
String response = "This could also be one of your product objects of type Product";
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);
messagingTemplate.convertAndSendToUser(sessionId,"/queue/product/changes", response, headerAccessor.getMessageHeaders());
}
}
客户端订阅更改
stompClient.subscribe('/user/queue/product/changes', function (scoredata) {
// We received product changes
});
有关详细信息,您还可以检查以下答案: https://stackoverflow.com/a/26288475/11133168
For detailed information you can also check this answer: https://stackoverflow.com/a/26288475/11133168
但是,如果您真的要考虑使用rest控制器来开始注册您的进程,或者如果它不满足您的要求,则应查看下面的链接. Spring还能够通过公开的SimpUserRegistry bean跟踪活动的Websocket会话及其用户.但是,您将需要根据应用程序的安全性为客户端输入通道配置自定义ChannelInterceptor适配器,以确定用户. 检查此答案以获取详细信息和代码示例: https://stackoverflow.com/a/45359294/11133168
However, if you really want to consider using a rest controller to start registering your process, or if it just doesn't meet your requirements, you should look at the link below. Spring is also able to track active websocket sessions and their users through an exposed SimpUserRegistry bean. However, you will need to configure a custom ChannelInterceptor adapter for your client input channel, depending on the security of your applications, to determine a user. Check this answer for detailed information and code examples: https://stackoverflow.com/a/45359294/11133168
您还可以订阅特定的产品ID主题,因此您甚至无需知道要通知特定产品更改的用户.
You could also subscribe to a specific product id topic so you don't even need to know which user wants to be notified about changes for a specific product.
//e.g if you want to be notified about changes for products with id 5
stompClient.subscribe('/product/changes/5', function (scoredata) {
// We received product changes
});
服务器服务示例
@Service
public class WebSocketProductService{
@Autowired
private SimpMessagingTemplate simpMessagingTemplate;
// This would be the method which should inform your clients about specific product
// changes, instead of the String parameters a Product object should be used instead,
// you have to call this method yourself on product changes or schedule it or sth.
public void sendProductChange(String product, String productId) {
this.simpMessagingTemplate.convertAndSend("/product/changes/"+productId, product);
}
}
服务器控制器
如果要管理产品ID订阅列表,则需要.如解决方案1中所述,您需要一个用@Controller注释的类,该类包含一个用@SubscribeMapping注释的方法.如果客户端尝试订阅指定的路径,则会调用此方法.
Server Controller
Needed if you want to manage a list of product id subscriptions. Like explained in solution 1 you need a class annotated with @Controller which contains a method annotated with @SubscribeMapping. This method gets called if a a client tries to subscribe to the specified path.
@Controller
public class WebSocketContoller{
@SubscribeMapping("/product/changes/{productId}")
public void productIdSubscription(@DestinationVariable Long productId) {
//Manage your product id subscription list e.g.
}
}