在Spring websockets中发送错误消息

Kar*_*hik 4 spring stomp spring-messaging spring-websocket

我试图通过SockJS在STOMP的Spring websockets中发送错误消息.

我基本上试图实现这里正在做的事情.

这是我的异常处理程序

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

而且我订阅了

stompClient.subscribe('/user/queue/error', stompErrorCallback, {token: accessToken});
Run Code Online (Sandbox Code Playgroud)

在我的情况下,用户未经过身份验证,但是从此处开始

虽然用户目的地通常意味着经过身份验证的用户,但并不严格要求.与经过身份验证的用户无关的WebSocket会话可以订阅用户目标.在这种情况下,@ SendToUser注释的行为与broadcast = false完全相同,即仅针对发送正在处理的消息的会话.

当我抛出这个错误时,所有这一切都正常,myHandler这是我在websocket config中定义的Websocket Handler.

我有一个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");
    }
}
Run Code Online (Sandbox Code Playgroud)

@MessageExceptionHandler没有抓到这个例外.所以我尝试直接使用它发送给用户simpMessagingTemplate.

我基本上想做:

simpMessagingTemplate.convertAndSendToUser(SOMETHING,"/queue/error",e);
Run Code Online (Sandbox Code Playgroud)

SOMETHING应该是正确的用户名,但用户在我的情况下未经过身份验证,因此我无法使用headerAccessor.getUser().getName()

我甚至尝试过

simpMessagingTemplate.convertAndSendToUser(headerAccessor.getHeader("","/queue/error",e, Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER, headerAccessor.getSessionId()));
Run Code Online (Sandbox Code Playgroud)

但这不起作用.

我甚至尝试过headerAccessor.getSessionId()用户名,但这似乎不起作用.

这样做的正确方法是什么?

我应该使用什么作为用户名convertAndSendToUser

Kar*_*hik 6

我最初的直觉是正确的,在未经身份验证的用户情况下,sessionId用作用户名,但问题在于标题.

经过几个小时的调试后@SendToUsersimpMessagingTemplate.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);
Run Code Online (Sandbox Code Playgroud)

它没有用,我试过把标题作为 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);
Run Code Online (Sandbox Code Playgroud)

也没用.

经过一些调试后,我发现了设置标头的正确方法,这可能是创建这些标头的唯一方法(来自SendToMethodReturnValueHandler.java).

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

最后,

String sessionId = headerAccessor.getSessionId();
template.convertAndSendToUser(sessionId,"/queue/error","tesssssts",createHeaders(sessionId));
Run Code Online (Sandbox Code Playgroud)

做了伎俩.