我是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.
}
}
如何实施?
答案 0 :(得分:2)
首先我的问题是,当您成功将Websocket与stomp集成时,为什么要尝试向剩余控制器发送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.
}
}
答案 1 :(得分:0)
春天documentation是学习Web套接字概念的一个很好的起点。 要发送给客户,您可以使用SimpMessageSendingOperations。
@Autowired
private SimpMessageSendingOperations messageSendingOperations;
可以通过控制器方法通过以下方式发送消息:
messageSendingOperations.convertAndSendToUser(websocketUserId, "/product/changes", messageObject);
答案 2 :(得分:0)
如果仅在用户请求产品更新时才向他们发送产品更新,则可以使用常规的HTTP请求。但我知道您想根据特定于用户的业务逻辑推送通知。您还必须实现E:.
+---BuildProcessTemplates
\---Src
+---AgentApiServer
| +---Splyce.AgentApi.Common
| | +---bin
| | | \---Debug
| | | \---netcoreapp2.1
| | +---MirrorEngine
| | | \---Framework
| | | +---Dtos
| | | +---Enums
| | | +---Events
| | | \---Interfaces
| | \---obj
| | \---Debug
| | \---netcoreapp2.1
| \---Splyce.AgentApi.Server
| +---bin
| | \---Debug
| | +---netcoreapp2.0
| | +---netcoreapp2.1
| | \---netcoreapp2.2
| +---obj
| | \---Debug
| | +---netcoreapp2.0
| | +---netcoreapp2.1
| | \---netcoreapp2.2
| +---Properties
| \---wwwroot
\---Standard
+---Common
+---packages
| +---NUnit.3.11.0
| | +---build
| | \---lib
| | +---net20
| | +---net35
| | +---net40
| | +---net45
| | +---netstandard1.4
| | \---netstandard2.0
| +---NUnit3TestAdapter.3.13.0
| | \---build
| | +---net35
| | \---netcoreapp1.0
| \---Shouldly.3.0.2
| \---lib
| +---net40
| +---net451
| +---netstandard1.3
| \---netstandard2.0
+---Splyce.Standard.Common
| +---bin
| | \---Debug
| | \---netstandard2.0
| +---Helpers
| \---obj
| \---Debug
| \---netstandard2.0
+---Splyce.Standard.Common.Tests
| +---bin
| | +---Debug
| | \---Release
| +---obj
| | \---Debug
| | \---TempPE
| \---Properties
+---Splyce.Standard.DbMigrations
| +---bin
| | \---Debug
| | \---netstandard2.0
| +---Dtos
| +---Framework
| | +---DbUp
| | +---Dtos
| | | \---DatabaseMigrationsManager
| | +---Enums
| | \---Events
| \---obj
| \---Debug
| \---netstandard2.0
+---Splyce.Standard.Messaging
| +---bin
| | \---Debug
| | \---netstandard2.0
| +---Client
| | +---Publisher
| | \---Subscriber
| +---Framework
| | +---Interfaces
| | \---Messages
| +---Messages
| | +---Health
| | \---Mirroring
| \---obj
| \---Debug
| \---netstandard2.0
\---UnitTestProject1
+---bin
| \---Debug
| \---netcoreapp2.2
\---obj
\---Debug
\---netcoreapp2.2
来对用户进行身份验证。
解决方案
我建议使用Spring Security
表在您的后端中添加此业务逻辑-与user_product_updates( user_id, product_id)
的用户想要订阅更新的product_id
对应的每一行:>
user_id
现在,您可以运行计划的后端作业(根据您的推送通知的业务逻辑,这可以是 cron作业)向您的用户发送更新:
@GetMapping("product-{id}")
public void startSubscribe(@PathVariable("id") Long id) {
// Save this custom setting into your models
}
展望未来,您可能需要添加一些缓存以对其进行优化(尤其是从@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) {
config.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)
}