Rails 6 API使用ActionCable报Subscription class not found错误
运行环境
- Ruby 版本:
ruby 3.0.2p107 - Rails 版本:
Rails 6.1.6 - 技术栈:Rails 作为 API 后端,React TypeScript 作为前端
问题复现代码
后端代码
app/channels/notification_channel.rb
class NotificationChannel < ApplicationCable::Channel def subscribed stream_from "notification_channel_#{current_user.user_id}" end def unsubscribed; end end
app/channels/application_cable/connection.rb
module ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_user end private def find_user current_user = Profile::Account.find_by(user_id: request.query_parameters[:user_id]) current_user ? current_user : reject_unauthorized_connection end end end
前端代码
const createSubscription = (consumer: ActionCable.Cable) => { consumer.subscriptions.create({ channel: `notification_channel_${user.sub}` }, { connected: () => console.log(`connected to notifications.${user.sub}`), received: (notification) => handleReceivedNotification(notification), }); }; const handleReceivedNotification = (notification: any) => { console.log(JSON.stringify(notification)); } useEffect((): void => { if (user) setComsumer(ActionCable.createConsumer(`ws://localhost:3100/cable?user_id=${user.sub}`)); }, [user]); useEffect((): void => { if (consumer) createSubscription(consumer); }, [consumer]);
异常表现
启动服务后WebSocket连接可以正常建立,但存在以下问题:
- 服务端日志抛出订阅类找不到的错误:
Registered connection (Z2lkOi8vYXBpL1Byb2ZpbGU6OkFjY291bnQvMQ) Subscription class not found: "notification_channel_facebook|10157537128720479"
- 控制台执行广播操作返回0,消息无法推送到前端:
$ ActionCable.server.broadcast "notification_channel_#{user.user_id}", { action: "new_notification" } D, [2022-07-07T14:44:03.820530 #19220] DEBUG -- : [ActionCable] Broadcasting to notification_channel_facebook|10157537128720479: {:action=>"new_notification"} => 0
- Redis pubsub频道列表中无
notification_channel相关频道:
Redis.new.pubsub("channels") => ["action_cable/Z2lkOi8vYXBpL1Byb2ZpbGU6OkFjY291bnQvMQ", "_action_cable_internal"]
相关配置
config/cable.yml
default: &default adapter: redis url: redis://localhost:6379/1 development: <<: *default test: <<: *default production: adapter: redis url: <%= ENV['REDIS_URL'] %> channel_prefix: api_production
config/application.rb
require "rails" require "active_model/railtie" require "active_job/railtie" require "active_record/railtie" require "active_storage/engine" require "action_controller/railtie" require "action_mailer/railtie" require "action_view/railtie" require "action_cable/engine" # require "sprockets/railtie" require "rails/test_unit/railtie" require "active_storage/engine" require "action_mailbox/engine"
问题根因
ActionCable客户端创建订阅时,传入的channel参数必须和后端定义的Channel类名(驼峰格式)完全匹配,服务端会根据这个参数路由到对应的Channel类处理订阅请求。
当前前端代码错误地将流名称notification_channel_${user.sub}作为channel参数传递,服务端收到请求后会尝试查找名为notification_channel_facebook|10157537128720479的Channel类,自然匹配失败抛出Subscription class not found错误。
由于Channel类匹配失败,NotificationChannel的subscribed方法不会执行,stream_from逻辑未触发,客户端没有订阅对应的广播流,因此广播时返回0(无订阅者),Redis中也看不到对应的频道。
修复方案
- 修改前端订阅创建逻辑,
channel固定传入后端类名NotificationChannel,用户ID作为自定义参数传递:
const createSubscription = (consumer: ActionCable.Cable) => { consumer.subscriptions.create( { channel: "NotificationChannel", user_id: user.sub }, { connected: () => console.log(`connected to notifications.${user.sub}`), received: (notification) => handleReceivedNotification(notification), } ); };
- (可选优化)调整后端
subscribed方法,从订阅参数中获取用户ID拼接流名称,和前端传参保持一致:
class NotificationChannel < ApplicationCable::Channel def subscribed stream_from "notification_channel_#{params[:user_id]}" end def unsubscribed; end end
修复后重启Rails服务,重新建立WebSocket连接即可正常订阅、接收广播消息。
内容的提问来源于stack exchange,提问作者Andrea Reginato
相关产品推荐
相关产品推荐

