使用stream_for 方法快速更正您的示例。 stream_for 需要一个模型,所以你的例子应该是
class ChatChannel < ApplicationCable::Channel
def subscribed
room = Room.find params[:room]
stream_for room
end
end
注意:
您的方法仍然有效,唯一的区别是流的命名广播。正如您很快就会看到的那样,这将更有意义。
stream_for 和stream_from 之间有什么区别吗?我不会说太多,但要实事求是地回答这个问题,让我们看看它们在幕后是如何工作的。
获取模型并反过来调用stream_from。结果是一个可序列化的字符串,用作命名广播。所以当你在一个
上调用stream_from
- 模型,命名广播成为模型全局id
- hash,命名广播变成查询参数
- 字符串,广播的名字变成字符串本身
所以 stream_for 像这样stream_for -> broadcasting_for -> stream_from
broadcasting_for 是 ActionCable 用来为应用程序中的对象生成唯一命名广播的机制。这可确保每个对象在您的应用程序生命周期中都有一个唯一标识符。
对于 ActiveRecord 模型,rails 在它们上调用to_gid_param,这会返回该模型实例的唯一标识符。如果您更新模型、更改属性、重新加载模型、在该模型上调用 to_gid_param 将始终返回相同的字符串。您可以在 Rails 控制台中尝试此操作,在 AR 模型实例上调用 to_gid_param。
对于其他类型的对象,rails 调用 to_param 将对象转换为 URL 安全查询参数。一种特殊情况是数组,请参阅how rails handle that here。
这里的根本区别是您不需要传递模型,只需传递一个字符串作为命名广播就可以了。如果你传递的不是字符串,it still gets converted to a string。
澄清你的疑虑
需要指出的一件有趣的事情是,使用 stream_for 方法,您仍然可以使用 ActionCable.server.broadcast("chat:#{room}", data) (注意将 _ 更改为 :) 但您不能使用ChatChannel.broadcast_to 与 stream_from 方法。
好吧,broadcast_to 最终还是会调用 ActionCable.server.broadcast,如果您希望在所有频道中都出现这种行为,您可以在基类中添加一个 broadcast 类方法,通常是 ApplicationCable::Channel
module ApplicationCable
class Channel < ActionCable::Channel::Base
def self.broadcast(broadcasting, data)
ActionCable.server.broadcast(broadcasting, data)
end
end
end
您甚至可以通过在此处统一 broadcast_to 来更进一步,因为您可以在您的频道中访问 braodcasting_for。在你的 Rails 控制台中尝试 ChatChannel.broadcasting_for(arg) 传递不同的值,包括模型,看看我的意思。
stream_from 更“危险”,因为它设置了一个需要作者维护的约定的全局房间。此外,广播只能直接从 ActionCable.server 完成。
就像我之前说的,您可以绕过这个,请参阅上面的简单解决方案。至于stream_for更危险,你怎么定义危险?如果您的意思是因为您必须自己维护命名广播,我将为您提供有意义的实例。
在您的示例中,您说stream_from 设置了一个全局房间。
请注意,不会建立订阅,除非您的客户端代码明确订阅您的频道。因此,即使您使用stream_for "chat_#{params[:room]}",除非您的客户端代码订阅房间,否则永远不会建立连接。
如果我同时拥有 ChatChannel 和 NotificationChannel 并且都包含 stream_from 'common',我将能够使用 ActionCable.server.broadcast('common', data) 同时在两个频道上进行广播。不过,这对我来说似乎是一种代码味道。
ActionCable 文档建议至少 a consumer should be subscribed to one channel。
你如何选择使用stream_from 会导致代码异味,这不是 ActionCable/Rails 的问题。
什么时候应该使用stream_from?
如果您计划制作复杂的命名广播以根据角色或某些标准将消息路由到客户端,使用stream_from 可以帮助您。这将确保只有满足这些定义标准的用户才能获得广播。
例子
我想向房间里的每个人广播一条消息
class ChatChannel < ApplicationCable::Channel
def subscribed
room = Room.find_by_name params[:room]
stream_for room
end
end
客户最终会像这样订阅这个频道
consumer.subscriptions.create({ channel: "ChatChannel", room: "gaming" })
每个客户都会收到此消息。
如果您想在不寻求复杂解决方案的情况下强制实施某种安全性怎么办?我们可以使用stream_from 来实现某种隐私。假设您想向游戏室中的特定用户发送消息,我们可以像这样制作一个命名广播
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_#{params[:room]}_#{params[:user]}"
end
end
客户最终会像这样订阅上述频道
consumer.subscriptions.create({ channel: "ChatChannel", room: "gaming", user: "1" })
这样如果你将消息广播到上述广播,只有 ID 为 1 的用户会收到消息。