You need to enable JavaScript to run this app. RoR自定义Devise会话控制器 渲染登录页触发表单nil值报错咨询
优惠活动
大模型
产品
解决方案
定价
更多

RoR自定义Devise会话控制器 渲染登录页触发表单nil值报错咨询

报错核心原因

这个报错的触发逻辑完全和你代码中的执行路径对应:

  • Devise登录页的表单使用form_for(resource, ...)渲染,要求第一个参数必须是有效的模型实例,不能为nil。正常通过GET请求访问登录页时,Devise会在new action中自动通过build_resource({})初始化一个空的User对象赋值给resource变量,供视图渲染表单使用。
  • 你在create action中实现了自定义的用户邮箱查询逻辑:self.resource = User.find_by(email: params[:user][:email]),当查询不到对应用户时,resource变量会被直接赋值为nil。
  • 结合Rails的render机制特性:执行render 'new'时仅会加载new对应的视图模板,不会触发new action的代码执行流程,所有视图可访问的变量都来自当前create action的上下文。此时视图拿到nil作为表单参数,直接触发“First argument in form cannot contain nil or be empty”报错。
修复方案

在用户不存在的分支中,手动对齐Devise的资源初始化逻辑,给视图传入有效的resource对象,修改app/controllers/users/sessions_controller.rb中create方法的空用户分支:

def create
  self.resource = User.find_by(email: params[:user][:email])
  if resource.nil?
    # 调用Devise内置方法初始化空的认证模型实例
    self.resource = build_resource({})
    # 回填用户已输入的邮箱值,避免用户重复输入
    resource.email = params[:user][:email]
    # 给模型添加校验错误,页面可自动同步展示提示
    resource.errors.add(:email, "未查询到匹配的注册用户,请核对邮箱后重试")
    render 'new'
  else
    # 原有登录、OTP发送逻辑保持不变
    set_flash_message!(:notice, :signed_in)
    sign_in(resource_name, resource)
    yield resource if block_given?
    resource.update(language: Constant::SUPPORTED_LANGUAGES.key(params['locale'])) if params['locale'].present?
    resource.send_otp(force_expire: true) if resource.email_verified?
    respond_with resource, location: after_sign_in_path_for(resource)
    flash.delete(:notice)
  end  
end
注意事项
  • 优先使用Devise提供的build_resource方法初始化资源,不要直接写User.new,前者会自动适配你当前配置的认证模型,后续调整认证逻辑时不需要同步修改控制器代码。
  • 你引入的ExpireExistingToken关注点模块的before_action仅挂载在new action上,render时不会触发该前置逻辑,不会造成token清理的逻辑冲突。
  • 因为你是实现仅邮箱登录的流程,原warden带密码校验的逻辑注释是合理的,不需要额外恢复。

内容的提问来源于stack exchange,提问作者Anisha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.03 06:24:11