Отправка сообщения об ошибке в веб-сокетах Spring
Я пытаюсь отправить сообщения об ошибках в Spring Websockets с STOMP через SockJS.
Я в основном пытаюсь добиться того, что делается здесь.
Это мой обработчик исключений
@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});
Пользователь в моем случае не аутентифицирован, но отсюда
Хотя назначения пользователей обычно подразумевают аутентифицированного пользователя, это строго не требуется. Сеанс WebSocket, который не связан с аутентифицированным пользователем, может подписаться на назначение пользователя. В таких случаях аннотация @SendToUser будет вести себя точно так же, как и с broadcast=false, то есть нацелена только на сеанс, который отправил обрабатываемое сообщение.
Все это прекрасно работает, когда я выкидываю эту ошибку из myHandler
это мой обработчик Websocket, определенный в конфигурации websocket.
у меня есть ClientInboundChannelInterceptor
который расширяется ChannelInterceptorAdapter
который перехватывает все сообщения в 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);
Что-то должно быть правильным именем пользователя, но в моем случае пользователь не аутентифицирован, поэтому я не могу использовать headerAccessor.getUser().getName()
Я даже пытался с
simpMessagingTemplate.convertAndSendToUser(headerAccessor.getHeader("","/queue/error",e, Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER, headerAccessor.getSessionId()));
но это не работает.
Я даже пытался headerAccessor.getSessionId()
вместо имени пользователя, но это не похоже на работу.
Как правильно это сделать?
Что я должен использовать в качестве имени пользователя в convertAndSendToUser
?
2 ответа
Моя первоначальная интуиция была правильной, sessionId используется в качестве имени пользователя в случае неаутентифицированных пользовательских ситуаций, но проблема была с заголовками.
После нескольких часов отладки @SendToUser
а также simpMessagingTemplate.convertAndSendToUser()
Я понял, что если мы используем @SendToUser
Заголовки будут установлены автоматически, и мы должны явно определить заголовки, если мы используем simpMessagingTemplate.convertAndSendToUser()
,
@SendToUser
устанавливал два заголовка,
simpMessageType: SimpMessageType.MESSAGE, simpSessionId: SESSIONID
Итак, я попытался добавить заголовки,
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);
Это не сработало, я попытался дать заголовки как 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);
тоже не работал
После некоторой дополнительной отладки я узнал, как правильно установить заголовки, и, вероятно, это единственный способ создать эти заголовки (из 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));
сделал трюк.
Ты можешь использовать
convertAndSendToUser()
только если этот пользователь подписан на пункт назначения:super.convertAndSend(this.destinationPrefix + user + destination, payload, headers, postProcessor);
куда
user
может быть простоsessionId
-headerAccessor.getSessionId()
@MessageExceptionHandler
делает свою работу только в@MessageMapping
или же@SubscribeMapping
,
Увидеть SendToMethodReturnValueHandler
Исходный код для получения дополнительной информации.