在 Spring websockets 中发送错误消息
Sending Error message in Spring websockets
我正在尝试通过 SockJS 在 Spring websockets 中使用 STOMP 发送错误消息。
我基本上是在努力实现正在做的事情here。
这是我的异常处理程序
@MessageExceptionHandler
@SendToUser(value = "/queue/error",broadcast = false)
public ApplicationError handleException(Exception message) throws ApplicationError {
return new ApplicationError("test");
}
我正在订阅
stompClient.subscribe('/user/queue/error', stompErrorCallback, {token: accessToken});
我的用户未通过身份验证,但来自 here
While user destinations generally imply an authenticated user, it
isn’t required strictly. A WebSocket session that is not associated
with an authenticated user can subscribe to a user destination. In
such cases the @SendToUser annotation will behave exactly the same as
with broadcast=false, i.e. targeting only the session that sent the
message being handled.
当我从 myHandler
抛出此错误时,所有这些工作正常,这是我在 websocket 配置中定义的 Websocket 处理程序。
我有一个扩展 ChannelInterceptorAdapter
的 ClientInboundChannelInterceptor
,它拦截 preSend
.
中的所有消息
万一这个拦截器有任何异常,我想把它扔回发送这条消息的用户会话,
public class ClientInboundChannelInterceptor extends ChannelInterceptorAdapter {
@Autowired
@Lazy(value = true)
@Qualifier("brokerMessagingTemplate")
private SimpMessagingTemplate simpMessagingTemplate;
@Override
public Message<?> preSend(Message message, MessageChannel channel) throws IllegalArgumentException{
if(some thing goes wrong)
throw new RuntimeException();
}
@MessageExceptionHandler
@SendToUser(value = "/queue/error",broadcast = false)
public ApplicationError handleException(RuntimeException message) throws ApplicationError {
return new ApplicationError("test");
}
}
@MessageExceptionHandler
没有捕捉到这个异常。所以我尝试使用 simpMessagingTemplate
直接将其发送给用户。
我基本上想做:
simpMessagingTemplate.convertAndSendToUser(SOMETHING,"/queue/error",e);
SOMETHING 应该是正确的用户名,但在我的情况下用户未通过身份验证,所以我不能使用 headerAccessor.getUser().getName()
我什至尝试过
simpMessagingTemplate.convertAndSendToUser(headerAccessor.getHeader("","/queue/error",e, Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER, headerAccessor.getSessionId()));
但这不起作用。
我什至试过 headerAccessor.getSessionId()
代替用户名,但这似乎不起作用。
正确的做法是什么?
我应该在 convertAndSendToUser
中使用什么作为用户名?
您可以使用 convertAndSendToUser()
只有当该用户订阅了目的地:
super.convertAndSend(this.destinationPrefix + user + destination, payload, headers, postProcessor);
其中 user
可以只是 sessionId
- headerAccessor.getSessionId()
@MessageExceptionHandler
仅与 @MessageMapping
或 @SubscribeMapping
一起工作。
有关详细信息,请参阅 SendToMethodReturnValueHandler
源代码。
我最初的直觉是正确的,在未经身份验证的用户情况下,sessionId 用作用户名,但问题出在 headers。
经过 @SendToUser
和 simpMessagingTemplate.convertAndSendToUser()
几个小时的调试后,我意识到如果我们使用 @SendToUser
headers 将自动设置,我们必须明确定义headers 如果我们使用 simpMessagingTemplate.convertAndSendToUser()
.
@SendToUser
设置了两个 headers,
simpMessageType:SimpMessageType.MESSAGE,simpSessionId:sessionId
所以我尝试添加 headers,
String sessionId = headerAccessor.getSessionId();
Map<String,Object> headerMap = new HashMap<>();
headerMap.put("simpMessageType", SimpMessageType.MESSAGE);
headerMap.put("simpSessionId",sessionId);
simpMessagingTemplate.convertAndSendToUser(headerAccessor.getSessionId(),"/queue/error",e,headerMap);
没用,我试过将 headers 设为 MessageHeaders
String sessionId = headerAccessor.getSessionId();
Map<String,Object> headerMap = new HashMap<>();
headerMap.put("simpMessageType", SimpMessageType.MESSAGE);
headerMap.put("simpSessionId",sessionId);
MessageHeaders headers = new MessageHeaders(headerMap);
simpMessagingTemplate.convertAndSendToUser(headerAccessor.getSessionId(),"/queue/error",e,headers);
也没用。
经过更多调试后,我找到了设置 headers 的正确方法,并且可能这是创建这些 headers(来自 SendToMethodReturnValueHandler.java
)的唯一方法。
private MessageHeaders createHeaders(String sessionId) {
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);
return headerAccessor.getMessageHeaders();
}
最后,
String sessionId = headerAccessor.getSessionId();
template.convertAndSendToUser(sessionId,"/queue/error","tesssssts",createHeaders(sessionId));
成功了。
我正在尝试通过 SockJS 在 Spring websockets 中使用 STOMP 发送错误消息。
我基本上是在努力实现正在做的事情here。
这是我的异常处理程序
@MessageExceptionHandler
@SendToUser(value = "/queue/error",broadcast = false)
public ApplicationError handleException(Exception message) throws ApplicationError {
return new ApplicationError("test");
}
我正在订阅
stompClient.subscribe('/user/queue/error', stompErrorCallback, {token: accessToken});
我的用户未通过身份验证,但来自 here
While user destinations generally imply an authenticated user, it isn’t required strictly. A WebSocket session that is not associated with an authenticated user can subscribe to a user destination. In such cases the @SendToUser annotation will behave exactly the same as with broadcast=false, i.e. targeting only the session that sent the message being handled.
当我从 myHandler
抛出此错误时,所有这些工作正常,这是我在 websocket 配置中定义的 Websocket 处理程序。
我有一个扩展 ChannelInterceptorAdapter
的 ClientInboundChannelInterceptor
,它拦截 preSend
.
万一这个拦截器有任何异常,我想把它扔回发送这条消息的用户会话,
public class ClientInboundChannelInterceptor extends ChannelInterceptorAdapter {
@Autowired
@Lazy(value = true)
@Qualifier("brokerMessagingTemplate")
private SimpMessagingTemplate simpMessagingTemplate;
@Override
public Message<?> preSend(Message message, MessageChannel channel) throws IllegalArgumentException{
if(some thing goes wrong)
throw new RuntimeException();
}
@MessageExceptionHandler
@SendToUser(value = "/queue/error",broadcast = false)
public ApplicationError handleException(RuntimeException message) throws ApplicationError {
return new ApplicationError("test");
}
}
@MessageExceptionHandler
没有捕捉到这个异常。所以我尝试使用 simpMessagingTemplate
直接将其发送给用户。
我基本上想做:
simpMessagingTemplate.convertAndSendToUser(SOMETHING,"/queue/error",e);
SOMETHING 应该是正确的用户名,但在我的情况下用户未通过身份验证,所以我不能使用 headerAccessor.getUser().getName()
我什至尝试过
simpMessagingTemplate.convertAndSendToUser(headerAccessor.getHeader("","/queue/error",e, Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER, headerAccessor.getSessionId()));
但这不起作用。
我什至试过 headerAccessor.getSessionId()
代替用户名,但这似乎不起作用。
正确的做法是什么?
我应该在 convertAndSendToUser
中使用什么作为用户名?
您可以使用
convertAndSendToUser()
只有当该用户订阅了目的地:super.convertAndSend(this.destinationPrefix + user + destination, payload, headers, postProcessor);
其中
user
可以只是sessionId
-headerAccessor.getSessionId()
@MessageExceptionHandler
仅与@MessageMapping
或@SubscribeMapping
一起工作。
有关详细信息,请参阅 SendToMethodReturnValueHandler
源代码。
我最初的直觉是正确的,在未经身份验证的用户情况下,sessionId 用作用户名,但问题出在 headers。
经过 @SendToUser
和 simpMessagingTemplate.convertAndSendToUser()
几个小时的调试后,我意识到如果我们使用 @SendToUser
headers 将自动设置,我们必须明确定义headers 如果我们使用 simpMessagingTemplate.convertAndSendToUser()
.
@SendToUser
设置了两个 headers,
simpMessageType:SimpMessageType.MESSAGE,simpSessionId:sessionId
所以我尝试添加 headers,
String sessionId = headerAccessor.getSessionId();
Map<String,Object> headerMap = new HashMap<>();
headerMap.put("simpMessageType", SimpMessageType.MESSAGE);
headerMap.put("simpSessionId",sessionId);
simpMessagingTemplate.convertAndSendToUser(headerAccessor.getSessionId(),"/queue/error",e,headerMap);
没用,我试过将 headers 设为 MessageHeaders
String sessionId = headerAccessor.getSessionId();
Map<String,Object> headerMap = new HashMap<>();
headerMap.put("simpMessageType", SimpMessageType.MESSAGE);
headerMap.put("simpSessionId",sessionId);
MessageHeaders headers = new MessageHeaders(headerMap);
simpMessagingTemplate.convertAndSendToUser(headerAccessor.getSessionId(),"/queue/error",e,headers);
也没用。
经过更多调试后,我找到了设置 headers 的正确方法,并且可能这是创建这些 headers(来自 SendToMethodReturnValueHandler.java
)的唯一方法。
private MessageHeaders createHeaders(String sessionId) {
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);
return headerAccessor.getMessageHeaders();
}
最后,
String sessionId = headerAccessor.getSessionId();
template.convertAndSendToUser(sessionId,"/queue/error","tesssssts",createHeaders(sessionId));
成功了。