【问题标题】:Sending Error message in Spring websockets在 Spring websockets 中发送错误消息
【发布时间】:2016-02-21 17:17:59
【问题描述】:

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

我基本上是在尝试实现here

这是我的异常处理程序

@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});

在我的情况下,用户未经过身份验证,但来自 here

虽然用户目的地通常意味着经过身份验证的用户,但它 不是严格要求的。未关联的 WebSocket 会话 使用经过身份验证的用户可以订阅用户目的地。在 在这种情况下,@SendToUser 注释的行为将与 使用广播=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);

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

我什至尝试过

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

但这不起作用。

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

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

convertAndSendToUser 中的用户名应该使用什么?

【问题讨论】:

    标签: spring stomp spring-websocket spring-messaging


    【解决方案1】:

    我最初的直觉是正确的,在未经身份验证的用户情况下使用 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);
    

    它不起作用,我尝试将标题设置为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));
    

    成功了。

    【讨论】:

    • @Tobia 很高兴它有帮助:)
    • 这项工作,谢谢。更多细节:使用 Spring Framework 4.3.2。我的客户端是 STOMP 上的 Stomp.js Websocket。您需要要发送消息的其他用户的 simpSessionId。我的客户端:stompClient.subscribe('/user/queue/bancaria.readcard', function(messageOutput)... 和服务器:messagingTemplate.convertAndSendToUser(targetSessionId, "/queue/bancaria.readcard", message, headers); 我我暂时不使用 Spring Security。谢谢。
    【解决方案2】:
    1. 只有当该用户订阅目的地时,您才能使用convertAndSendToUser()

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

    3. @MessageExceptionHandler 仅与 @MessageMapping@SubscribeMapping 一起工作。

    查看SendToMethodReturnValueHandler源代码了解更多信息。

    【讨论】:

    • 我不知道你为什么说我们不能使用 convertAndSendToUser() ,但是以正确的方式设置标题是有效的。
    • 哦!对不起。是的,我的意思是“你可以”。本来要说你不能在拦截器中使用`@MessageExceptionHandler`,因为它为时过早。看我的#3
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-21
    • 1970-01-01
    • 2021-10-07
    • 2022-11-11
    • 2023-03-28
    • 1970-01-01
    相关资源
    最近更新 更多