java - 如何使用spring websocket向自定义用户发送自定义消息?

标签 java spring spring-boot spring-websocket sockjs

我是 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 集成时,为什么还要尝试向休息 Controller 发送 http 请求? 如果我正确理解了您的用例,我可以想到 atm 的三种解决方案。

解决方案 1(套接字 session ↔ 产品 ID)

您可以通过开放的 websocket 连接将您的请求直接从客户端发送到服务器。然后 Spring 可以确定哪个 Websocket session 进行了调用,您可以实现您的业务逻辑。您需要激活另一个名为“/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

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

但是,如果您真的想考虑使用休息 Controller 来开始注册您的进程,或者它只是不符合您的要求,您应该查看下面的链接。 Spring 还能够通过暴露的 SimpUserRegistry bean 跟踪 Activity 的 websocket session 及其用户。但是,您需要根据应用程序的安全性为客户端输入 channel 配置自定义 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);
    }
}

服务器 Controller

如果您想管理产品 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.
    }
}

关于java - 如何使用spring websocket向自定义用户发送自定义消息?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54763261/

相关文章:

java - 如何在 JPA 中接受 Map<String,Object> 类型的元组

java - 如何根据 Activity 配置文件访问 application-{profile}.properties 文件

java - Guice 如何将依赖项注入(inject)从动态加载的类实例化的对象

java - SOAP 客户端对 JAVA 中给定 WSDL 的奇怪响应

java - Flyway Maven 插件从外部配置文件执行 GOLS

java - @RefreshScope 可以渴望吗

spring-boot - EmbeddedKafka kafka streams 用SpringBootTest测试发现两个StreamsBuilderFactoryBeans

java - 在一个类中构建对象并在另一个类中访问它们

java - 如何在Java中制作一个实心矩形?

java - 手动创建 SolrCrudRepository