使用令牌和状态连接到Phoenix Socket

时间:2017-12-22 22:18:27

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

我试图将Phoenix 频道令牌 Presence 模块捆绑在一起,为我的Phoenix 1.3添加聊天功能应用。我还没有能够让所有3个模块一起工作。最后一个错误是connection to websocket closed before handshake。现在,我没有收到任何错误,但它也没有连接到套接字。

我认为这个问题是" connect"在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

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

更新

我正在使用&#34; current_player&#34;插件将播放器结构存储在conn中作为&#34; 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

1 个答案:

答案 0 :(得分:0)

您的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按字面分配原子:after_join,而不是使用当前玩家ID的实际值,但根本不需要此行。相反,在您的{: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更改为{:ok, _} = Presence.track(socket, socket.assigns.current_player.id, %{ username: socket.assigns.current_player.username, online_at: inspect(System.system_time(:seconds)) }) 。这是因为您无法将结构指定为Presence键。

或者你可以做

socket.js

并在您first.username id中使用renderOnlineUsers代替{{1}}内的{{1}}