簡體   English   中英

如何使用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 的三種解決方案。

方案一(socket session ↔ product id)

您可以通過開放的 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


解決方案 2(主體 ↔ 產品 ID)

但是,如果您真的想考慮使用 rest 控制器來開始注冊您的流程,或者它不符合您的要求,您應該查看下面的鏈接。 Spring 還能夠通過公開的 SimpUserRegistry bean 跟蹤活動的 websocket 會話及其用戶。 但是,您需要為客戶端輸入通道配置自定義 ChannelInterceptor 適配器,具體取決於應用程序的安全性,以確定用戶。 檢查此答案以獲取詳細信息和代碼示例: https : //stackoverflow.com/a/45359294/11133168


解決方案 3(產品 ID 主題)

您還可以訂閱特定的產品 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM