Rails6部署Heroku触发Stimulus Reflex报ActionCable连接未打开错误如何解决
报错核心原因
ActionCable 的 WebSocket 连接在用户触发 Stimulus Reflex 操作时还未完成初始化,或 Heroku 生产环境的 ActionCable 配置存在限制导致连接未正常建立。
解决步骤
1. 前端优化触发逻辑,等待连接建立后再开放交互
新增 Stimulus 控制器控制单选按钮的可用状态,避免连接未就绪时触发操作:
在 app/javascript/controllers/preps_controller.js 中添加以下代码:
import { Controller } from "@hotwired/stimulus" import StimulusReflex from 'stimulus_reflex' export default class extends Controller { connect() { StimulusReflex.register(this) if (this.isActionCableConnectionOpen()) { this.enableRadios() } else { document.addEventListener('cable:connected', () => this.enableRadios()) } } enableRadios() { this.element.querySelectorAll('input[type=radio]').forEach(radio => { radio.disabled = false }) } togglePrep() { if (this.isActionCableConnectionOpen()) { this.stimulate('PrepsReflex#toggle_prep', event.target) } } }
修改 more_info.html.erb 模板,绑定控制器并初始禁用单选按钮:
<div class='preps' data-controller="preps"> <%= f.input :heads_prep, label_html: {class:"label"}, item_wrapper_class: "radio-label", input_html: { disabled: true, data: { action: "change->preps#togglePrep", prepValue: 'heads', activity: @activity.id } }, collection: Activity.heb_preps.map{ |k, v| [v, k] }, as: :radio_buttons %> <%= f.input :guides_prep, label_html: {class:"label"}, item_wrapper_class: "radio-label", input_html: { disabled: true, data: { action: "change->preps#togglePrep", prepValue: 'guides', activity: @activity.id } }, collection: Activity.heb_preps.map{ |k, v| [v, k] }, as: :radio_buttons %> </div>
2. 修复 Heroku 生产环境 ActionCable 配置
修改 config/environments/production.rb,添加 WebSocket 相关配置:
# 允许WebSocket跨域请求 config.action_cable.allowed_request_origins = [/https:\/\/.*\.herokuapp\.com/] # 配置ActionCable服务地址,APP_DOMAIN替换为你的Heroku应用域名 config.action_cable.url = "wss://#{ENV['APP_DOMAIN']}/cable"
检查 config/routes.rb 确认已挂载 ActionCable:
Rails.application.routes.draw do # 其他路由保持不变 mount ActionCable.server => '/cable' end
在 Heroku 后台的配置变量中添加 APP_DOMAIN,值为你的应用域名,格式如 xxx.herokuapp.com。
3. 后端 Reflex 代码优化(可选)
添加空值安全调用,避免销毁不存在的 Mifal 记录时报错:
# frozen_string_literal: true class PrepsReflex < ApplicationReflex delegate :current_user, to: :connection def toggle_prep activity = Activity.find(element.dataset.activity) prep = element.dataset.prepvalue if element.value == 'yes' activity.update("#{prep}_prep": :yes) activity.mifals.find_or_create_by(mifal_type: prep) do |mifal| mifal.name = "#{Mifal.heb_mifal_type(prep)} #{activity.name}" end else activity.update("#{prep}_prep": :no) activity.mifals.find_by(mifal_type: prep)&.destroy end end end
内容的提问来源于stack exchange,提问作者Guy Natan
相关产品推荐
相关产品推荐

