Отправка сообщения об ошибке в веб-сокетах 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));

сделал трюк.

  1. Ты можешь использовать convertAndSendToUser() только если этот пользователь подписан на пункт назначения:

    super.convertAndSend(this.destinationPrefix + user + destination, payload, headers, postProcessor);
    
  2. куда user может быть просто sessionId - headerAccessor.getSessionId()

  3. @MessageExceptionHandler делает свою работу только в @MessageMapping или же @SubscribeMapping,

Увидеть SendToMethodReturnValueHandler Исходный код для получения дополнительной информации.

Другие вопросы по тегам