You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails聊天应用Turbo Stream广播时current_user识别异常问题

Rails Turbo Stream 广播消息时用户识别异常的解决办法

问题描述

开发聊天应用,Message模型包含content、user_id、sender_id、receiver_id字段,已实现两位用户间消息过滤功能。但通过Turbo Stream广播消息时,新消息在双方页面都显示为当前用户发送,刷新页面后才恢复正常。尝试使用Current属性仍未解决。

核心原因

Turbo Stream在后端执行广播时,Current.user(或current_user)指向的是消息发送者,后端渲染的局部视图会基于这个用户生成HTML,推送给所有订阅该通道的客户端,导致所有客户端收到的都是发送者视角的消息样式。

解决方案

方案一:为单个用户单独广播(推荐)

分别给发送者和接收者推送对应视角的消息,避免后端统一渲染导致的用户混淆。

  1. 修改Message模型的广播逻辑:
class Message < ApplicationRecord
  scope :get_messages, ->(user_1, user_2) {
    where("(sender_id = ? AND receiver_id = ?) OR (sender_id = ? AND receiver_id = ?)", user_1, user_2, user_2, user_1)
  }

  after_create_commit do
    # 给消息发送者广播,标记为自己发送的消息
    broadcast_append_to current_user, 
                       partial: "messages/message", 
                       locals: { message: self, is_sender: true }
    # 给消息接收者广播,标记为对方发送的消息
    broadcast_append_to User.find(receiver_id), 
                       partial: "messages/message", 
                       locals: { message: self, is_sender: false }
  end
end
  1. 修改消息局部视图,不再依赖后端的Current.user,改用传入的is_sender变量判断:
<% if is_sender %>
  <div class="flex justify-end">
    <div class="p-3 m-2 rounded-lg max-w-32 bg-sky-300">
      <h3><%= message.body %></h3>
    </div>
  </div>
<% else %>
  <div class="flex justify-start">
    <div class="p-3 m-2 rounded-lg max-w-32 bg-green-300">
      <h3><%= message.body %></h3>
    </div>
  </div>
<% end %>
  1. 修改消息列表视图,订阅当前用户的专属通道:
<%= turbo_stream_from current_user %>
<div id="messages" class="h-[32rem] overflow-auto">
  <%= render @messages %>
</div>

方案二:按对话创建专属广播通道

给每一对聊天用户创建唯一的通道,同时在前端用JS判断当前用户是否为发送者(避免后端渲染依赖用户身份)。

  1. 修改Message模型的广播逻辑:
class Message < ApplicationRecord
  scope :get_messages, ->(user_1, user_2) {
    where("(sender_id = ? AND receiver_id = ?) OR (sender_id = ? AND receiver_id = ?)", user_1, user_2, user_2, user_1)
  }

  after_create_commit do
    # 生成唯一对话ID,确保两个用户订阅同一通道
    conversation_id = [sender_id, receiver_id].sort.join("_")
    broadcast_append_to "conversation_#{conversation_id}", 
                       partial: "messages/message", 
                       locals: { message: self }
  end
end
  1. 修改消息局部视图,嵌入发送者ID,用前端JS判断样式:
<div class="message-item" data-sender-id="<%= message.sender_id %>">
  <div class="p-3 m-2 rounded-lg max-w-32">
    <h3><%= message.body %></h3>
  </div>
</div>

<script>
  // 页面加载时初始化样式,Turbo Stream更新后重新判断
  document.addEventListener('turbo:load', () => {
    const currentUserId = <%= current_user.id %>;
    document.querySelectorAll('.message-item').forEach(item => {
      const senderId = parseInt(item.dataset.senderId);
      if (senderId === currentUserId) {
        item.classList.add('flex', 'justify-end');
        item.querySelector('div').classList.add('bg-sky-300');
      } else {
        item.classList.add('flex', 'justify-start');
        item.querySelector('div').classList.add('bg-green-300');
      }
    });
  });
</script>
  1. 修改消息列表视图,订阅对应对话的通道:
<% conversation_id = [current_user.id, @other_user.id].sort.join("_") %>
<%= turbo_stream_from "conversation_#{conversation_id}" %>
<div id="messages" class="h-[32rem] overflow-auto">
  <%= render @messages %>
</div>

注意事项

  • 确保Message模型存在receiver_id字段(你的scope代码中已使用该字段),如果没有需要先添加迁移。
  • 方案一的current_user在模型中可用的前提是你已经通过Current属性或其他方式在模型层注入了当前用户(比如自己实现的Current模块)。

内容的提问来源于stack exchange,提问作者MiNHAZ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 08:40:59