1. Overview
1.概述
In this quick tutorial, we’re going to illustrate how to send a message to a specific session or particular user using Spring WebSockets.
在这个快速教程中,我们将说明如何使用Spring WebSockets向特定会话或特定用户发送消息。
For an introduction to the above module, please refer to this article.
关于上述模块的介绍,请参考到这篇文章。
2. WebSocket Configuration
2.WebSocket配置
First of all, we need to configure our message broker and WebSocket application endpoint:
首先,我们需要配置我们的消息代理和WebSocket应用程序端点。
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig
extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic/", "/queue/");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/greeting");
}
}
With @EnableWebSocketMessageBroker we enabled a broker-backed messaging over WebSocket using STOMP, which stands for Streaming Text Oriented Messaging Protocol. It’s important to remark that this annotation needs to be used in conjunction with the @Configuration.
通过@EnableWebSocketMessageBroker,我们使用STOMP在WebSocket上启用了经纪人支持的消息传递,它代表面向文本的消息传递协议。需要指出的是,这个注释需要与@Configuration一起使用。
It isn’t mandatory to extend the AbstractWebSocketMessageBrokerConfigurer but, for the quick example, it’s easier to customize the imported configuration.
并非一定要扩展AbstractWebSocketMessageBrokerConfigurer,但对于快速示例来说,定制导入的配置更容易。
In the first method, we set up a simple memory-based message broker to carry the messages back to the client on destinations prefixed with “/topic” and “/queue”.
在第一种方法中,我们设置了一个简单的基于内存的消息代理,在以“/topic”和“/queue”为前缀的目的地上将消息带回客户端。
And, in the second, we registered stomp endpoints at “/greeting”.
其次,我们在“/greeting”处注册了跺脚端点。
In case that we want to enable SockJS, we have to amend the register part:
如果我们想启用SockJS,我们必须修改注册部分。
registry.addEndpoint("/greeting").withSockJS();
3. Get Session ID by Interceptor
3.通过拦截器获取会话ID
One way to obtain the session id is adding a Spring Interceptor which will be trigger during the handshake and get the information from the request data.
获取会话ID的一个方法是添加一个Spring拦截器,它将在握手过程中被触发并从请求数据中获取信息。
This interceptor can be added directly in WebSocketConfig:
这个拦截器可以直接添加到WebSocketConfig中:。
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("/greeting")
.setHandshakeHandler(new DefaultHandshakeHandler() {
public boolean beforeHandshake(
ServerHttpRequest request,
ServerHttpResponse response,
WebSocketHandler wsHandler,
Map attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest
= (ServletServerHttpRequest) request;
HttpSession session = servletRequest
.getServletRequest().getSession();
attributes.put("sessionId", session.getId());
}
return true;
}}).withSockJS();
}
4. WebSocket Endpoint
4.WebSocket端点
Starting with Spring 5.0.5.RELEASE, it isn’t necessary to do any customization because of the improvement of @SendToUser annotation, that allows us to send a message to a user destination via “/user/{sessionId}/…” rather than “/user/{user}/…“.
从Spring 5.0.5.RELEASE开始,由于改进了@SendToUser注解,我们可以通过”/user/{sessionId}/…“而不是”/user/{user}/…“向用户目的地发送消息,这就不需要做任何定制了。
That means the annotation works relying on the session id of the input message, effectively sending a reply to destination private to the session:
这意味着注释的工作依赖于输入消息的会话ID,有效地发送一个回复到目的地的私人会话。
@Controller
public class WebSocketController {
@Autowired
private SimpMessageSendingOperations messagingTemplate;
private Gson gson = new Gson();
@MessageMapping("/message")
@SendToUser("/queue/reply")
public String processMessageFromClient(
@Payload String message,
Principal principal) throws Exception {
return gson
.fromJson(message, Map.class)
.get("name").toString();
}
@MessageExceptionHandler
@SendToUser("/queue/errors")
public String handleException(Throwable exception) {
return exception.getMessage();
}
}
It’s import to remark that, @SendToUser indicates that the return value of a message-handling method should be sent as a Message to the specified destination(s) prepended with “/user/{username}“.
值得注意的是,@SendToUser表示一个消息处理方法的返回值应该作为Message发送到指定的目的地,前缀为”/user/{username}“。
5. WebSocket Client
5.WebSocket客户端
function connect() {
var socket = new WebSocket('ws://localhost:8080/greeting');
ws = Stomp.over(socket);
ws.connect({}, function(frame) {
ws.subscribe("/user/queue/errors", function(message) {
alert("Error " + message.body);
});
ws.subscribe("/user/queue/reply", function(message) {
alert("Message " + message.body);
});
}, function(error) {
alert("STOMP error " + error);
});
}
function disconnect() {
if (ws != null) {
ws.close();
}
setConnected(false);
console.log("Disconnected");
}
A new WebSocket is created pointing to “/greeting” for the mapping in WebSocketConfiguration.
一个新的WebSocket被创建,指向/greeting,用于WebSocketConfiguration中的映射。
When we subscribe the client to “/user/queue/errors” and “/user/queue/reply” is where we use the remarked information from the last section.
当我们把客户端订阅到”/user/queue/errors“和”/user/queue/reply“时,就是我们使用上一节中的注释信息的地方。
As we can see, @SendToUser points to “queue/errors” but the message will be sent to “/user/queue/errors“.
我们可以看到,@SendToUser指向”queue/errors“,但信息将被发送到”/user/queue/errors“。
6. Conclusion
6.结语
In this article, we’have explored a way to send a message directly to a user or session id with Spring WebSocket
在这篇文章中,我们探讨了用Spring WebSocket直接向用户或会话ID发送消息的方法
As always, the full source code of the examples is available over on GitHub.
一如既往,这些示例的完整源代码可在GitHub上获得over。