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,推送给所有订阅该通道的客户端,导致所有客户端收到的都是发送者视角的消息样式。
解决方案
方案一:为单个用户单独广播(推荐)
分别给发送者和接收者推送对应视角的消息,避免后端统一渲染导致的用户混淆。
- 修改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
- 修改消息局部视图,不再依赖后端的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 %>
- 修改消息列表视图,订阅当前用户的专属通道:
<%= turbo_stream_from current_user %> <div id="messages" class="h-[32rem] overflow-auto"> <%= render @messages %> </div>
方案二:按对话创建专属广播通道
给每一对聊天用户创建唯一的通道,同时在前端用JS判断当前用户是否为发送者(避免后端渲染依赖用户身份)。
- 修改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
- 修改消息局部视图,嵌入发送者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>
- 修改消息列表视图,订阅对应对话的通道:
<% 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
相关产品推荐
相关产品推荐

