Saltar al contenido

¿Cómo enviar un mensaje personalizado a un usuario personalizado con Spring Websocket?

Solución:

Mi pregunta en primer lugar sería, ¿por qué está intentando enviar una solicitud http a un controlador de descanso cuando integró con éxito websockets con stomp? Si entiendo correctamente su caso de uso, debería haber tres soluciones en las que pueda pensar en atm.

Solución 1 (sesión de socket ↔ id de producto)

Puede enviar su solicitud directamente desde su cliente al servidor a través de la conexión websocket abierta. Spring puede entonces determinar qué sesión de Websocket realizó la llamada y usted puede implementar su lógica empresarial. Debe activar otro corredor llamado “/ queue” y especificar el prefijo para el destino del usuario que se necesita cuando una suscripción no está destinada a una transmisión. En el lado del cliente, también debe cambiar su ruta de suscripción. Finalmente, debe crear una clase que esté comentada con @Controller que contenga sus asignaciones de mensajes para recibir mensajes del cliente conectado.

Configuración del servidor

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/websocket").withSockJS();
    }
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/queue", "/product");  // <- added "/queue"
        registry.setApplicationDestinationPrefixes("/app");
        registry.setUserDestinationPrefix("/user");
    }
}

Controlador de servidor

@Controller
public class WebSocketContoller{
    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    @MessageMapping("/product/register")
    public void register(@Payload Long productId, @Header("simpSessionId") String sessionId) {
        // register current websocket session with product id and 
        // then with convertAndSendToUser send changes to current user.

        // Example of how to send a message to the user using the sessionId
        String response = "This could also be one of your product objects of type Product";
        SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
        headerAccessor.setSessionId(sessionId);
        headerAccessor.setLeaveMutable(true);

        messagingTemplate.convertAndSendToUser(sessionId,"/queue/product/changes", response, headerAccessor.getMessageHeaders());
    }
}

Cambio de suscripción de cliente

stompClient.subscribe('/user/queue/product/changes', function (scoredata) {
    // We received product changes
});

Para obtener información detallada, también puede consultar esta respuesta: https://stackoverflow.com/a/26288475/11133168


Solución 2 (principal ↔ identificación del producto)

Sin embargo, si realmente desea considerar el uso de un controlador de descanso para comenzar a registrar su proceso, o si simplemente no cumple con sus requisitos, debe mirar el enlace a continuación. Spring también puede rastrear sesiones activas de websocket y sus usuarios a través de un bean SimpUserRegistry expuesto. Sin embargo, deberá configurar un adaptador ChannelInterceptor personalizado para el canal de entrada de su cliente, dependiendo de la seguridad de sus aplicaciones, para determinar un usuario. Consulte esta respuesta para obtener información detallada y ejemplos de código: https://stackoverflow.com/a/45359294/11133168


Solución 3 (temas de identificación de producto)

También puede suscribirse a un tema de identificación de producto específico para que ni siquiera necesite saber qué usuario desea ser notificado sobre cambios para un producto específico.

Cambio de suscripción de cliente

//e.g if you want to be notified about changes for products with id 5 
stompClient.subscribe('/product/changes/5', function (scoredata) {
    // We received product changes
});

Ejemplo de servicio de servidor

@Service
public class WebSocketProductService{

    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    // This would be the method which should inform your clients about specific product     
    // changes, instead of the String parameters a Product object should be used instead, 
    // you have to call this method yourself on product changes or schedule it or sth.
    public void sendProductChange(String product, String productId) {
        this.simpMessagingTemplate.convertAndSend("/product/changes/"+productId, product);
    }
}

Controlador de servidor

Necesario si desea administrar una lista de suscripciones de ID de producto. Como se explica en la solución 1, necesita una clase anotada con @Controller que contiene un método anotado con @SubscribeMapping. Este método se llama si un cliente intenta suscribirse a la ruta especificada.

@Controller
public class WebSocketContoller{
    @SubscribeMapping("/product/changes/{productId}")
    public void productIdSubscription(@DestinationVariable Long productId) {
        //Manage your product id subscription list e.g.
    }
}

Si quieres enviar Actualizaciones de Producto a los usuarios solo cuando los usuarios los soliciten, entonces puede usar solicitudes HTTP normales. Pero entiendo que quieres notificaciones push basado en una lógica empresarial específica del usuario. También debes estar implementando Spring Security para autenticar a sus usuarios.


Solución

Propongo agregar esta lógica de negocios en su backend usando un user_product_updates( user_id, product_id) tabla – cada fila corresponde a un product_id que un usuario con user_id desea suscribirse a las actualizaciones:

@GetMapping("product-{id}")
public void startSubscribe(@PathVariable("id") Long id) {
    // Save this custom setting into your models
}

Ahora puedes ejecutar un trabajo de backend programado (que puede ser un trabajo cron basado en la lógica empresarial de su notificaciones push) para enviar actualizaciones a sus usuarios:

@Autowired 
org.springframework.messaging.simp.SimpMessagingTemplate simpMessagingTemplate;   

@Scheduled(cron = "0 0 1 * * ?") // based on your business logic (say daily at 12:01 am)
public void scheduleTaskUsingCronExpression() {
   // loop through user_product_updates table and construct "data"
   // username is from your spring security username (principal.getName())
   simpMessagingTemplate.convertAndSendToUser(username, "/queue/products", data);
}

En el futuro, es posible que desee agregar algunos cachés para optimizarlos (especialmente para obtener información del producto de product_id) para que las cosas funcionen sin problemas.


Resumen

Su configuración de web-socket:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/websocket").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.setApplicationDestinationPrefixes("/app")
            .setUserDestinationPrefix("/user")
            .enableSimpleBroker("/topic", "/queue", "/product");
    }
}

Su oyente en la aplicación frontend puede verse así:

that.stompClient.subscribe("/user/queue/products", (message) => {
    if (message.body) {
      // We received product changes
    }
});

Los usuarios se registrarán para recibir actualizaciones de productos:

@GetMapping("product-{id}")
public void startSubscribe(@PathVariable("id") Long id) {
    // Save to your persistence module
    // (that the particular user wants updates from such-and-such products)
}

El trabajo del programador de backend enviará actualizaciones cuando estén disponibles:

@Scheduled(cron = "0 0 1 * * ?") // based on your business logic
public void scheduleTaskUsingCronExpression() {
   // loop through user_product_updates table and construct "data"
   // username is from your spring security username (principal.getName())
   template.convertAndSendToUser(username, "/queue/products", data);
}
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *