问题
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:
You can use
convertAndSendToUser()only if that user is subscribed to the destination:super.convertAndSend(this.destinationPrefix + user + destination, payload, headers, postProcessor);Where
usercan be justsessionId-headerAccessor.getSessionId()The
@MessageExceptionHandlerdoes its work only withing@MessageMappingor@SubscribeMapping.
See SendToMethodReturnValueHandler source code for more info.
来源:https://stackoverflow.com/questions/33806014/sending-error-message-in-spring-websockets