有没有办法从服务器端(控制器)终止对任何特定使用者的特定通道的订阅,以便可以调用我的咖啡脚本文件中的断开连接回调?
class ChatChannel < ApplicationCable::Channel
def subscribed
@room = Chat::Room[params[:room_number]]
reject unless current_user.can_access?(@room)
end
end
在致电reject
之前,您还可以告知订户拒绝原因:
class ChatChannel < ApplicationCable::Channel
def subscribed
if params["answerer"]
answerer = params["answerer"]
answerer_user = User.find_by email: answerer
if answerer_user
stream_from "chat_#{answerer_user}_channel"
else
connection.transmit identifier: params, error: "The user #{answerer} not found."
# http://api.rubyonrails.org/classes/ActionCable/Channel/Base.html#class-ActionCable::Channel::Base-label-Rejecting+subscription+requests
reject
end
else
connection.transmit identifier: params, error: "No params specified."
# http://api.rubyonrails.org/classes/ActionCable/Channel/Base.html#class-ActionCable::Channel::Base-label-Rejecting+subscription+requests
reject
end
end
end
之前的答案允许您拒绝订阅频道的尝试。但是,它们不会让您在订阅后强行取消订阅连接。例如,用户可能会从聊天室中退出,因此您需要取消他们对聊天室频道的订阅。我想出了this Pull Request到Rails来支持这个。
基本上它为remote_connections
添加了取消订阅方法,因此您可以调用:
subscription_identifier = "{\"channel\":\"ChatChannel\", \"chat_id\":1}"
remote_connection = ActionCable.server.remote_connections.where(current_user: User.find(1))
remote_connection.unsubscribe(subscription_identifier)
这会在internal_channel
(所有连接都已订阅)上发送消息,相关连接通过删除其对指定通道的订阅来响应。
你可以做这样的事情。
class YourChannel < ApplicationCable::Channel
#your code
def your_custom_action
if something
reject_subscription
end
end
end
正如Ollie的回答正确指出的那样,这里的其他答案是在成功之前拒绝ActionCable连接,但问题是在订阅之后断开订阅。
这个问题非常重要,因为它处理的是用户被赶出他以前所在的聊天室的情况。除非你将他与该订阅断开连接,否则他将继续通过WebSocket接收该频道的消息,直到他关闭他的窗口/ tab或重新加载页面(因为这将启动一个新的订阅,服务器将不会订阅他没有权限的聊天)。
Ollie的回答指向了他提出的一个很棒的拉取请求,因为它允许断开特定的流,而不是用户拥有的所有打开的WebSockets连接;问题是它还没有在Rails中合并。
我的解决方案是使用已存在的文档化API功能。即使很难让您选择要断开的流,也可以从用户断开所有打开的websocket连接。
在我的测试中,这很好用,因为一旦断开连接,所有选项卡将在几秒内尝试重新订阅,并且它将在每个ActionCable通道中触发subscribed
方法,从而重新启动连接,但现在基于最多来自服务器的最新权限(当然,不会将他重新订阅到他被踢出的聊天中)。
解决方案是这样的,假设您有一个连接记录ChatroomUser,用于跟踪特定用户是否可以读取特定聊天室中的聊天:
class ChatroomUser < ApplicationRecord
belongs_to :chatroom
belongs_to :user
after_destroy :disconnect_action_cable_connections
private
def disconnect_action_cable_connections
ActionCable.server.remote_connections.where(current_user: self.user).disconnect
end
end
这使用了这个API(https://api.rubyonrails.org/classes/ActionCable/RemoteConnections.html),并假设你在ApplicationCable :: Connection中设置了current_user
,就像大多数人一样(根据教程)。