Отправка данных из почтового запроса на выход веб-сокета в Play Framework (Scala)

Мне нужна помощь с Play Framework. У меня есть этот веб-сокет, который немедленно отвечает сообщением клиенту. Мой API должен иметь возможность получать сообщения от другого API, который отправляет их через запрос POST.

Он должен следовать такой логике:

  1. Я получаю сообщение от POST-запроса от уже существующего API
  2. Он передает это сообщение функции WebSocket, или Flow, или тому, что мне нужно сделать.
  3. WebSocket выводит это сообщение клиенту.
  4. Клиент (в данном случае приложение Angular) получает сообщение от WebSocket.

Есть ли способ передать данные из почтового запроса в веб-сокет, чтобы он отправил их клиенту?

Я пробовал только тот же веб-сокет, но не смог зайти слишком далеко. Я оставил так, как написано в документации. На данный момент он отвечает только «Я получил ваше сообщение» клиенту, отправившему это сообщение.

Я оставлю здесь рассматриваемый код, функция post имеет комментарий «// подключаем WebSocket». Это то место, где мне нужно отправить это сообщение на выход WebSocket, чтобы оно достигло клиента в режиме реального времени. Я читал документацию много раз, но она не особо помогла. Ему не хватает многого.

(Я не думаю, что это должно работать так, но это то, что приказал мой начальник... По этой причине я не пробовал отправлять другому клиенту через тот же WebSocket)


package controllers

import org.apache.pekko.actor._
import org.apache.pekko.stream.Materializer
import play.api.libs.streams.ActorFlow

import javax.inject._
import play.api.mvc._


@Singleton
class HomeController @Inject()(val controllerComponents: ControllerComponents) (implicit system: ActorSystem, mat: Materializer) extends BaseController {

    def index(): Action[AnyContent] = Action { implicit request: Request[AnyContent] =>
        Ok(views.html.index())
    }

    def getMessage: Action[AnyContent] = Action { request: Request[AnyContent] =>
        println(request.body.toString)

        //connect the websocket here
        
        Ok("It works!")
    }

    def socket: WebSocket = WebSocket.accept[String, String] { request =>
        ActorFlow.actorRef { out => MyWebSocketActor.props(out) }
    }
}

object MyWebSocketActor {
    def props(out: ActorRef): Props = Props(new MyWebSocketActor(out))
}

class MyWebSocketActor(out: ActorRef) extends Actor {
    def receive: Receive = {
        case msg: String =>
            out ! ("I received your message: " + msg)
    }
}
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я только что извлек часть кода из MarkCLewis — Play-Videos . Также есть плейлист на YouTube Play Framework с использованием Scala, в котором вы узнаете, как создать чат с использованием веб-сокетов.

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

Не уверен, насколько хорошо вы знакомы с моделью актера и акка / пекко . Play Framework построен на основе play akka или play pekko в зависимости от версии Play.

Вы можете представить актера как очередь сообщений, которая будет последовательно обрабатывать каждое сообщение. Вы не можете создать актера как новый экземпляр объекта, вам нужен ActorSystem, который позволит вам создавать актеров. Каждый раз, когда вы создаете новый объект с помощью ActorSystem, вы получаете ссылку на него. Единственный способ отправлять сообщения актеру — использовать эту ссылку.

val dummyActor = system.actorOf(Props[DummyActor], "unique-name-of-actor")
dummyActor ! SomeMessage(param1, param2, param3)

WebSocket — это актер, который может получать сообщение от клиента и отправлять сообщение клиенту, открывшему соединение через веб-сокет. В примере называется ChatActor. Нам понадобится второй актер (с именем ChatManager), который позволит нам отправлять сообщения ChatActor, используя его ссылку.

маршруты

Нечего объяснять. Две конечные точки

  • /: получить запрос GET и отправить сообщение на открытые веб-сокеты.
  • /ws: конечная точка для открытия соединения через веб-сокет.
GET  /    HelloController.index
GET  /ws  HelloController.socket

Контроллер

Просто базовый контроллер с двумя методами и одним полем.

  • manager: ChatManager, созданный с использованием ActorSystem, предоставленного игрой.

  • index: как только получен HTTP-запрос, мы отправляем сообщение ChatManager, который отправит сообщение каждому ChatActor, имеющему открытое соединение через веб-сокет.

  • socket: обработчик открытия соединения с веб-скокетом. Вы также можете видеть, что ChatActor получает параметр ActorManager by в своем конструкторе.

import org.apache.pekko.actor.{ActorSystem, Props}
import org.apache.pekko.stream.Materializer
import play.api.libs.streams.ActorFlow
import play.api.mvc._
import javax.inject.Inject

class HelloController @Inject() (cc: ControllerComponents)(implicit
    system: ActorSystem,
    mat: Materializer
) extends AbstractController(cc) {

  private val manager = system.actorOf(Props[ChatManager], "Manager")

  def index: Action[AnyContent] =
    Action { _ =>
      // this is the place where we sent a message to the manager.
      // Similar to what you need. You receive a POST (in this example
      // is a GET) and you send a message to the client connected to the
      // websocket
      manager ! ChatManager.Message("a hello message sent from index")
      Ok("hello")
    }

  def socket = WebSocket.accept[String, String] { _ =>
    ActorFlow.actorRef { out =>
      ChatActor.props(out, manager)
    }
  }
}

ЧатАктор

Как видите, как только актер создан, в первой строке мы отправили сообщение manager о том, что у нас есть новый чат. В методе receive мы рассматриваем два случая

  • в случае String мы отправили сообщение менеджеру
  • в случае получения SendMessage мы просто отправили сообщение через веб-сокет
import org.apache.pekko.actor.{Actor, ActorRef, Props}

class ChatActor(out: ActorRef, manager: ActorRef) extends Actor {
  manager ! ChatManager.NewChatter(self)

  import ChatActor._
  def receive = {
    case s: String        => manager ! ChatManager.Message(s)
    case SendMessage(msg) => out ! msg
  }
}

object ChatActor {
  def props(out: ActorRef, manager: ActorRef) = Props(
    new ChatActor(out, manager)
  )

  case class SendMessage(msg: String)
}

ЧатМенеджер

У нас есть изменяемый список с именем chatters, который будет содержать все открытые соединения webscoket. Тогда рассмотрим два случая

  • когда получен NewChatter, мы просто добавляем новый элемент в список
  • когда Message получен, мы перебираем список chatters и отправляем Message каждому из них
import org.apache.pekko.actor.{Actor, ActorRef}
import scala.collection.mutable.ListBuffer

class ChatManager extends Actor {
  private val chatters = ListBuffer.empty[ActorRef]

  import ChatManager._
  def receive = {
    case NewChatter(chatter) => chatters += chatter
    case Message(msg)        => for (c <- chatters) c ! ChatActor.SendMessage(msg)
  }
}

object ChatManager {
  case class NewChatter(chatter: ActorRef)
  case class Message(msg: String)
}

Большое спасибо за ваш ответ, он определенно решил эту проблему для меня! Честно говоря, я не знаком с Пекко, моя компания начала мигрировать с NodeJS на playframework пару недель назад. Мне нужно задать пару вопросов: во-первых, как вы упомянули, это не подойдет для среднего или большого проекта. Есть ли какое-либо решение, которое мне следует иметь в виду, чтобы позже сделать его пригодным для более крупного проекта? И во-вторых, есть ли реальная разница между ActorSystems Pekko и Akka? Я знаю, что это форк, но отсутствие документации Pekko затрудняет изучение. Приветствую вас и еще раз спасибо!

DanCrts 07.06.2024 10:14

Здесь сложно дать развернутый ответ. Stackoverflow предназначен для конкретных вопросов с конкретными ответами. Я бы посоветовал задать вопрос на другой платформе, например reddit - scala , или вы можете попробовать какое-нибудь из сообществ Discord

Gastón Schabas 07.06.2024 16:11

Другие вопросы по теме