Sending Error message in Spring websockets

你说的曾经没有我的故事 提交于 2019-12-18 17:26:14

问题


I am trying to send error messages in Spring websockets with STOMP over SockJS.

I am basically trying to achieve which is being done here.

This is my Exception Handler

@MessageExceptionHandler
@SendToUser(value = "/queue/error",broadcast = false)
public ApplicationError handleException(Exception message) throws ApplicationError {
        return  new ApplicationError("test");
}

And I am subscribing to

stompClient.subscribe('/user/queue/error', stompErrorCallback, {token: accessToken});

User in my case is not authenticated, but from 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.

All this works fine when I am throwing this error from myHandler which is my Websocket Handler defined in websocket config.

I have a ClientInboundChannelInterceptor which extends ChannelInterceptorAdapter which intercepts all the messages in preSend.

In case of any exception in this interceptor, I want to throw it back to the user session which sent this message,

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 does not catch this exception. So I tried sending it to the user directly using simpMessagingTemplate.

I basically want to do :

simpMessagingTemplate.convertAndSendToUser(SOMETHING,"/queue/error",e);

SOMETHING should be the correct username but user is not authenticated in my case, so I can't use headerAccessor.getUser().getName()

I have even tried with

simpMessagingTemplate.convertAndSendToUser(headerAccessor.getHeader("","/queue/error",e, Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER, headerAccessor.getSessionId()));

but this is not working.

I have even tried headerAccessor.getSessionId() in the place of username, but that does not seem to work.

What is the correct way to do this?

What should I use as username in convertAndSendToUser?


回答1:


My initial intuition was correct, sessionId is used as the username in case of unauthenticated user situations, but the problem was with headers.

After few hours of debugging through @SendToUser and simpMessagingTemplate.convertAndSendToUser(), I realised that if we use @SendToUser headers will be set automatically and we have to explicitly define the headers if we are using simpMessagingTemplate.convertAndSendToUser().

@SendToUser was setting two headers,

simpMessageType:SimpMessageType.MESSAGE,simpSessionId:sessionId

So I have tried adding the 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);

It did not work, I have tried giving the headers as 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);

didn't work either.

After some more debugging I found out the correct way to set the headers, and probably this is the only way to create these headers(from SendToMethodReturnValueHandler.java).

private MessageHeaders createHeaders(String sessionId) {
    SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
    headerAccessor.setSessionId(sessionId);
    headerAccessor.setLeaveMutable(true);
    return headerAccessor.getMessageHeaders();
}

So finally,

String sessionId = headerAccessor.getSessionId();
template.convertAndSendToUser(sessionId,"/queue/error","tesssssts",createHeaders(sessionId));

did the trick.




回答2:


  1. You can use convertAndSendToUser() only if that user is subscribed to the destination:

    super.convertAndSend(this.destinationPrefix + user + destination, payload, headers, postProcessor);
    
  2. Where user can be just sessionId - headerAccessor.getSessionId()

  3. The @MessageExceptionHandler does its work only withing @MessageMapping or @SubscribeMapping.

See SendToMethodReturnValueHandler source code for more info.



来源:https://stackoverflow.com/questions/33806014/sending-error-message-in-spring-websockets

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!