Как отправить пользовательское сообщение обычному пользователю с помощью пружинного websocket? - PullRequest
0 голосов
/ 19 февраля 2019

Я новичок в пружинной паутине .Я хочу отправить изменения продукта клиентам.Для этого я хочу сделать это следующим образом: Клиент создает сокет-соединение и подписывает назначение:

var socket = new SockJS('/websocket');
var stompClient = Stomp.over(socket);

stompClient.connect({}, function (frame) {
    stompClient.subscribe('/product/changes', function (scoredata) {
        // We received product changes
    });
});
//Send Ajax request and say server I want to know product with id=5 changes.
sendAjaxRequest(5);

Я настроил приложение Spring следующим образом:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

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

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/product/");
        registry.setApplicationDestinationPrefixes("/app");
    }
}

Теперь мне нужноследующий метод:

@RestController
public class ProductController {

    @GetMapping("product-{id}")
    public void startSubscribe(@PathVariable("id") Long id) {

        // register current websocket session with product id and 
        // then with convertAndSendToUser send changes to current user.
    }

}

Как мне это реализовать?

Ответы [ 3 ]

0 голосов
/ 01 марта 2019

Во-первых, мой вопрос: почему вы пытаетесь отправить http-запрос на контроллер остальных, когда вы успешно интегрировали веб-сокеты с Stomp?Если я правильно понимаю ваш вариант использования, должно быть три решения, которые я могу придумать. Atm.

Решение 1 (сеанс сокета id идентификатор продукта)

Вы можете отправить свой запрос непосредственно от своего клиентана сервер через открытое соединение websocket.Затем Spring может определить, какой сеанс Websocket сделал вызов, и вы можете реализовать свою бизнес-логику.Вам необходимо активировать другого посредника с именем «/ queue» и указать префикс для цели пользователя, который необходим, когда подписка не предназначена для трансляции.На стороне клиента вы также должны изменить путь подписки.Наконец, вы должны создать класс с комментариями @Controller, который содержит ваши сопоставления сообщений для получения сообщений от подключенного клиента.

Конфигурация сервера

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

Контроллер сервера

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

Изменение клиентской подписки

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

Для получения подробной информации вы также можете проверить этот ответ: https://stackoverflow.com/a/26288475/11133168


Решение 2 (принципал id идентификатор продукта)

Однако, если вы действительно хотите рассмотреть возможность использования контроллера отдыха для начала регистрации вашего процесса или если он просто не соответствует вашим требованиям, вам следует обратиться по ссылке ниже.Spring также может отслеживать активные сеансы веб-сокетов и их пользователей с помощью открытого компонента SimpUserRegistry.Однако вам потребуется настроить собственный адаптер ChannelInterceptor для вашего клиентского входного канала, в зависимости от безопасности ваших приложений, чтобы определить пользователя.Проверьте этот ответ для получения подробной информации и примеров кода: https://stackoverflow.com/a/45359294/11133168


Решение 3 (темы идентификатора продукта)

Вы также можете подписаться на конкретную тему идентификатора продукта, чтобы вы не делали этого.даже нужно знать, какой пользователь хочет получать уведомления об изменениях для конкретного продукта.

Изменение подписки клиента

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

Пример службы сервера

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

Контроллер сервера

Необходим, если вы хотите управлять списком подписок на идентификаторы продуктов.Как объяснено в решении 1, вам нужен класс, аннотированный @Controller, который содержит метод, аннотированный @SubscribeMapping.Этот метод вызывается, если клиент пытается подписаться по указанному пути.

@Controller
public class WebSocketContoller{
    @SubscribeMapping("/product/changes/{productId}")
    public void productIdSubscription(@DestinationVariable Long productId) {
        //Manage your product id subscription list e.g.
    }
}
0 голосов
/ 02 марта 2019

Если вы хотите отправлять обновления продукта пользователям только тогда, когда пользователи их запрашивают, то вы можете использовать обычные HTTP-запросы.Но я понимаю, что вы хотите push-уведомления на основе бизнес-логики, специфичной для пользователя.Вы также должны внедрить Spring Security для аутентификации своих пользователей.


Решение

Я предлагаю добавить эту бизнес-логику в ваш бэкэнд, используя user_product_updates( user_id, product_id)таблица - каждая строка, соответствующая product_id, которую пользователь с user_id хочет подписать на обновления:

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

Теперь вы можете запустить запланированное задание серверной части (которое можетбыть заданием cron на основе бизнес-логики ваших push-уведомлений ) для отправки обновлений вашим пользователям:

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

В дальнейшем вы можете добавитьнекоторые кэши для их оптимизации (особенно получение информации о продукте из product_id), чтобы все работало гладко.


Сводка

Вашконфигурация веб-сокета:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

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

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

Ваш слушатель в приложении веб-интерфейса может выглядеть следующим образом:

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

Пользователи будут регистрироваться для получения обновлений продукта:

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

Backendзадание планировщика будет отправлять обновления по мере доступности:

@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);
}
0 голосов
/ 19 февраля 2019

Spring документация является хорошей отправной точкой для изучения концепций веб-сокетов.Для отправки клиенту вы можете использовать SimpMessageSendingOperations .

@Autowired
private SimpMessageSendingOperations messageSendingOperations;

Из метода контроллера сообщение может быть отправлено следующим образом:

messageSendingOperations.convertAndSendToUser(websocketUserId, "/product/changes", messageObject);
...