![](/img/trans.png)
[英]How to send message to user when he connects to spring websocket
[英]How to send custom message to custom user with spring websocket?
我是spring websocket 的新手。 我想將產品更改發送給客戶。 為此,我想這樣做:客戶端創建一個套接字連接並訂閱目的地:
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 應用程序配置如下:
@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.
}
}
我該如何實施?
首先我的問題是,當您成功將 websockets 與 stomp 集成時,為什么要嘗試向 rest 控制器發送 http 請求? 如果我正確理解了您的用例,那么我可以想到 atm 的三種解決方案。
您可以通過開放的 websocket 連接將您的請求直接從您的客戶端發送到服務器。 然后 Spring 可以確定哪個 Websocket 會話進行了調用,您可以實現您的業務邏輯。 您需要激活另一個名為“/queue”的代理,並為訂閱不用於廣播時所需的用戶目標指定前綴。 在客戶端,您還必須更改訂閱路徑。 最后,您必須創建一個用 @Controller 注釋的類,其中包含您的消息映射以從連接的客戶端接收消息。
@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
但是,如果您真的想考慮使用 rest 控制器來開始注冊您的流程,或者它不符合您的要求,您應該查看下面的鏈接。 Spring 還能夠通過公開的 SimpUserRegistry bean 跟蹤活動的 websocket 會話及其用戶。 但是,您需要為客戶端輸入通道配置自定義 ChannelInterceptor 適配器,具體取決於應用程序的安全性,以確定用戶。 檢查此答案以獲取詳細信息和代碼示例: https : //stackoverflow.com/a/45359294/11133168
您還可以訂閱特定的產品 ID 主題,這樣您甚至不需要知道哪個用戶希望收到有關特定產品更改的通知。
//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 注釋的方法。 如果客戶端嘗試訂閱指定路徑,則會調用此方法。
@Controller
public class WebSocketContoller{
@SubscribeMapping("/product/changes/{productId}")
public void productIdSubscription(@DestinationVariable Long productId) {
//Manage your product id subscription list e.g.
}
}
如果您只想在用戶請求時向用戶發送產品更新,那么您可以使用普通的 HTTP 請求。 但我知道您想根據特定於用戶的業務邏輯推送通知。 您還必須實現Spring Security
來驗證您的用戶。
解決方案
我建議使用user_product_updates( user_id, product_id)
表在您的后端添加此業務邏輯 - 每行對應於具有user_id
的用戶想要訂閱更新的product_id
:
@GetMapping("product-{id}")
public void startSubscribe(@PathVariable("id") Long id) {
// Save this custom setting into your models
}
現在,您可以運行計划的后端作業(可以是基於推送通知的業務邏輯的cron 作業)以向您的用戶發送更新:
@Autowired
org.springframework.messaging.simp.SimpMessagingTemplate simpMessagingTemplate;
@Scheduled(cron = "0 0 1 * * ?") // based on your business logic (say daily at 12:01 am)
public void scheduleTaskUsingCronExpression() {
// loop through user_product_updates table and construct "data"
// username is from your spring security username (principal.getName())
simpMessagingTemplate.convertAndSendToUser(username, "/queue/products", data);
}
展望未來,您可能需要添加一些緩存來優化它們(尤其是從product_id
獲取產品信息),以便一切順利運行。
您的網絡套接字配置:
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/websocket").withSockJS(); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.setApplicationDestinationPrefixes("/app") .setUserDestinationPrefix("/user") .enableSimpleBroker("/topic", "/queue", "/product"); } }
您在前端應用程序中的偵聽器可能如下所示:
that.stompClient.subscribe("/user/queue/products", (message) => { if (message.body) { // We received product changes } });
用戶將注冊產品更新:
@GetMapping("product-{id}") public void startSubscribe(@PathVariable("id") Long id) { // Save to your persistence module // (that the particular user wants updates from such-and-such products) }
后端調度程序作業將在可用時發送更新:
@Scheduled(cron = "0 0 1 * * ?") // based on your business logic public void scheduleTaskUsingCronExpression() { // loop through user_product_updates table and construct "data" // username is from your spring security username (principal.getName()) template.convertAndSendToUser(username, "/queue/products", data); }
Spring 文檔是學習 Web 套接字概念的一個很好的起點。 要發送到客戶端,您可以使用SimpMessageSendingOperations 。
@Autowired
private SimpMessageSendingOperations messageSendingOperations;
從控制器方法,消息可以通過以下方式發送:
messageSendingOperations.convertAndSendToUser(websocketUserId, "/product/changes", messageObject);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.