5

我正在尝试使用 STOMP over SockJS 在 Spring websockets 中发送错误消息。

我基本上是在努力实现这里正在做的事情。

这是我的异常处理程序

@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 注释的行为与使用广播 = false 时完全相同,即仅针对发送正在处理的消息的会话。

当我抛出这个错误时,所有这一切都很好,myHandler这是我在 websocket 配置中定义的 Websocket 处理程序。

我有一个ClientInboundChannelInterceptorwhich extendsChannelInterceptorAdapter拦截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);

SOMETHING应该是正确的用户名,但在我的情况下用户没有经过身份验证,所以我不能使用headerAccessor.getUser().getName()

我什至尝试过

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

但这不起作用。

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

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

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

4

2 回答 2

6

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

通过@SendToUserand调试几个小时后simpMessagingTemplate.convertAndSendToUser(),我意识到如果我们使用@SendToUserheaders 将自动设置,如果我们使用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));

成功了。

于 2015-11-20T06:48:09.077 回答
0
  1. convertAndSendToUser()只有当该用户订阅了目的地时,您才能使用:

    super.convertAndSend(this.destinationPrefix + user + destination, payload, headers, postProcessor);
    
  2. 哪里user可以sessionId——headerAccessor.getSessionId()

  3. 仅使用or@MessageExceptionHandler才起作用。@MessageMapping@SubscribeMapping

有关更多信息,请参阅SendToMethodReturnValueHandler源代码。

于 2015-11-19T19:44:41.857 回答