【问题标题】:Connect to Phoenix Socket with Token and Presence使用令牌和存在连接到 Phoenix Socket
【发布时间】:2018-06-05 11:44:45
【问题描述】:

我正在尝试将 Phoenix ChannelTokenPresence 模块结合在一起,以便为我的 Phoenix 1.3 应用程序添加聊天功能。我无法让所有 3 个模块一起工作。最后一个错误是connection to websocket closed before handshake。现在,我没有收到任何错误,但它也没有连接到套接字。

我认为问题在于 player_socket.ex 中的“连接”功能。 (我有一个播放器资源)。这是函数:

  def connect(%{"token" => token}, socket) do
      case Phoenix.Token.verify(socket, "player auth", token, max_age: @max_age) do
        {:ok, player_id} ->
          player = Repo.get!(Player, player_id)
          {:ok, assign(socket, :current_player, player)}
          {:error, _reason} ->
           :error
      end
  end

我正在 app.html.eex 的元标记中签署令牌。 <%= tag :meta, name: "channel_token", content: Phoenix.Token.sign(@conn, "player auth", :player_id) %>

然后在 lobby_channel.ex 我尝试加入频道:

  def join("lobby:lobby", _params, socket) do
    send(self(), :after_join)
    {:ok, assign(socket, :player_id, :current_player)}
  end

  def handle_info(:after_join, socket) do
    push socket, "presence_state", Presence.list(socket)
    {:ok, _} = Presence.track(socket, socket.assigns.current_player, %{
      online_at: inspect(System.system_time(:seconds))
    })
    {:noreply, socket}
  end

我阅读了文档,但似乎无法弄清楚为什么我无法使用“current_player”连接到 websocket,以便我可以使用 Presence 显示谁在线以及玩家的姓名与他们的聊天相关联消息。非常感谢任何见解!我在这里有回购: https://github.com/EssenceOfChaos/gofish

更新

我正在使用“current_player”插件将播放器结构存储在 conn 中作为“current_player”。

%Plug.Conn{adapter: {Plug.Adapters.Cowboy.Conn, :...},
 assigns: %{current_player: %Gofish.Accounts.Player{__meta__: #Ecto.Schema.Metadata<:loaded, "players">,
    email: "example@aol.com", id: 6,

这是我更新后的 lobby_channel.ex:

  def join("lobby:lobby", _params, socket) do
    send(self(), :after_join)
    {:ok, socket}
  end

  def handle_info(:after_join, socket) do
    push socket, "presence_state", Presence.list(socket)
    {:ok, _} = Presence.track(socket, socket.assigns.current_player.id, %{
      username: socket.assigns.current_player.username,
      online_at: inspect(System.system_time(:seconds))
    })
    {:noreply, socket}
  end

【问题讨论】:

    标签: sockets websocket elixir phoenix-framework phoenix-channels


    【解决方案1】:

    您的player_socket.ex 很好。不过,您确实有一些问题:

    在您的layout/app.eex 模板中:

    Phoenix.Token.sign(@conn, "player auth", :player_id) 字面意思是写一个原子:player_id 而不是玩家的ID。为了写入播放器的 ID,您应该使用 @player_id 并添加一个插件,将值全局分配给您的 router.ex,如下所示:

    pipeline :browser do
      [...]
      plug :fetch_current_user
    end
    
    ...
    
    def fetch_current_user(conn, _) do
      assigns(conn, :current_player, get_session(conn, :current_player)
    end
    

    这将使@current_player 在您的所有模板中可用,然后您可以在app.eex 中使用:

    <%= tag :meta, name: "channel_token", content: Phoenix.Token.sign(@conn, "player auth", @current_player) %>
    

    (如果@current_player 不是nil,你应该有条件地写这个,如果是,请阻止你的 JS 客户端尝试 websocket 连接,顺便说一句)

    只要您已登录,此更改将立即解决您无法连接到 websocket 的问题,但您还有一个问题: {:ok, assign(socket, :player_id, :current_player)} 在您的 loby_channel.ex 中是按字面意思分配原子 :current_player 而不是使用当前玩家 ID 的实际值,但您根本不需要这一行。相反,在您的:after_join 中,您应该这样做

    {:ok, _} = Presence.track(socket, socket.assigns.current_player.username, %{
      online_at: inspect(System.system_time(:seconds))
    })
    

    请注意,我将 socket.assigns.current_player 更改为 socket.assigns.current_player.username。这是因为您不能将结构分配为 Presence 键。

    你也可以这样做

    {:ok, _} = Presence.track(socket, socket.assigns.current_player.id, %{
      username: socket.assigns.current_player.username,
      online_at: inspect(System.system_time(:seconds))
    })
    

    在您的socket.js 中,您将在renderOnlineUsers 中使用first.username 而不是id

    【讨论】:

    • 感谢您抽出宝贵时间回答!你是对的,我分配了原子“player_id”而不是实际的 id。我没有注意到,因为我仍在获得令牌的价值。我现在已连接到 websocket,所以我肯定取得了一些进展: --- 传输:已连接到 ws://localhost:4000/socket/websocket?token=SFMyNTY.g3QAAAACZAAEZGF0YWEGZAAGc2lnbmVkbgYAcXFphWAB.JzSzRcaWpebtBk2kH5ooeqKr8ez3Lxi8IuyRkuh0qxU&vsn=2.0.0 un但 Presence 模块不显示在线用户。我正在尝试在 socket.js 中使用“first.username”
    • 啊,那是因为我忘了指出另一个错误:在您的 socket.js 中您有 let chatInput = document.querySelector("#chat-input"); 但它实际上应该是 let chatInput = document.querySelector("#chatInput"); 因为这就是您在您的模板:)
    • 现在显示的是登录用户。谢谢您的回答!如果我用另一个浏览器登录并注册一个新帐户,它会显示同一个用户登录了两次,知道为什么吗?
    • 这一定与我在元标记中使用“@current_player.id”来签署令牌有关
    • 您确定有问题吗?我看到两个不同的用户,因为它应该没有改变你的回购中的任何东西:i.imgur.com/shxViKI.png
    猜你喜欢
    • 2021-11-25
    • 1970-01-01
    • 2019-07-07
    • 1970-01-01
    • 2017-08-20
    • 2011-07-11
    • 1970-01-01
    • 2019-01-11
    • 2019-12-02
    相关资源
    最近更新 更多