Rails7+Devise+Captcha报错:undefined method `users_url',如何修复验证码校验逻辑?
问题背景
前置条件:
- Rails 7.0.4
- 支持Hotwire/Turbo的Devise 4.9.0
- Responders 3.1.0
Devise配置包含以下设置:
config.responder.error_status = :unprocessable_entity config.responder.redirect_status = :see_other
标准Devise注册流程正常运行,但添加验证码校验后出现异常。在ApplicationController中添加的校验代码如下:
class ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? # 仅在创建动作时校验验证码 prepend_before_action :check_captcha_on_sign_up, if: Proc.new { request.controller_class == Devise::RegistrationsController }, only: [:create] protected def check_captcha_on_sign_up if CaptchaJob.new.valid?(params[:"smart-token"], request.remote_ip) flash.delete :alert else build_resource(sign_up_params) resource.valid? clean_up_passwords(resource) flash.alert = "请确认您不是机器人" respond_with_navigational(resource) do render :new end end end end
验证码校验通过时流程正常,但校验不通过时抛出错误:undefined method 'users_url' for #<Devise::RegistrationsController:0x00000000036600>。异常详情显示:
ActionView::MissingTemplate: Missing template devise/registrations/new, devise/new, application/new with {:locale=>[:ru], :formats=>[:turbo_stream], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :jbuilder]}.
但devise/registrations/new.html.erb文件实际存在。完整Devise配置(无注释):
Devise.setup do |config| config.mailer_sender = '***' require 'devise/orm/active_record' config.case_insensitive_keys = [:email] config.strip_whitespace_keys = [:email] config.skip_session_storage = [:http_auth] config.stretches = Rails.env.test? ? 1 : 12 config.reconfirmable = true config.expire_all_remember_me_on_sign_out = true config.password_length = 6..128 config.email_regexp = /\A[^@\s]+@[^@\s]+\z/ config.reset_password_within = 6.hours config.sign_out_via = :delete config.responder.error_status = :unprocessable_entity config.responder.redirect_status = :see_other end
已尝试添加config.navigational_formats = ['*/*', :html, :turbo_stream]但无效,需修复check_captcha_on_sign_up方法,使校验不通过时能正确渲染new.html.erb视图。
修复方案
问题核心是Turbo提交表单时请求格式为turbo_stream,但现有视图仅支持html格式,且respond_with_navigational在Turbo环境下的处理逻辑不符合预期。修改check_captcha_on_sign_up方法如下:
def check_captcha_on_sign_up if CaptchaJob.new.valid?(params[:"smart-token"], request.remote_ip) flash.delete :alert else build_resource(sign_up_params) resource.valid? clean_up_passwords(resource) flash.now.alert = "请确认您不是机器人" # 明确指定渲染html格式的注册表单,并设置正确的错误状态码 render "devise/registrations/new", formats: [:html], status: :unprocessable_entity end end
关键调整点:
- 直接调用
render并指定formats: [:html],避免Turbo寻找不存在的turbo_stream模板 - 使用
flash.now替代flash,确保错误提示仅在当前请求内有效(渲染视图属于同一次请求,无需跨请求保留提示) - 设置
status: :unprocessable_entity,与Devise配置的错误状态码保持一致
修改后,验证码校验不通过时会正确渲染注册表单页面并显示错误提示,不再出现模板缺失或路由方法未定义的错误。
内容的提问来源于stack exchange,提问作者noff
相关产品推荐
相关产品推荐

