Rails使用Devise gem自定义注册流程,实现先填邮箱发确认链接再显示注册表单
Devise 自定义两步注册流程实现方案
前置准备
- 确保你已经正常安装并初始化了
devisegem,用户模型为默认的User - 先给User模型添加注册标识字段,执行迁移命令:
rails g migration AddRegistrationFieldsToUsers registration_token:string:index registration_token_expires_at:datetime confirmed_at:datetime rails db:migrate
第一步:调整模型与Devise基础配置
- 修改
app/models/user.rb,添加核心逻辑:
class User < ApplicationRecord # 保留你原有devise模块,新增confirmable模块 devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :confirmable # 邮箱格式校验 validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP } # 生成唯一注册token,默认24小时有效期 def generate_registration_token! self.registration_token = SecureRandom.urlsafe_base64(nil, false) self.registration_token_expires_at = 24.hours.from_now save! end # 校验token是否有效 def valid_registration_token? registration_token_expires_at > Time.now && encrypted_password.blank? end end
- 修改
config/initializers/devise.rb,关闭默认注册后自动登录逻辑:
config.sign_in_after_sign_up = false
第二步:配置路由与自定义控制器
- 修改
config/routes.rb,添加自定义路由:
Rails.application.routes.draw do # 重定向devise注册逻辑到自定义控制器 devise_for :users, controllers: { registrations: 'users/registrations' } # 第二步注册相关路由 get '/complete_registration', to: 'users/registrations#complete_registration', as: :complete_registration put '/update_registration', to: 'users/registrations#update_registration', as: :update_registration # 其他原有路由保留 end
- 新建自定义注册控制器
app/controllers/users/registrations_controller.rb:
class Users::RegistrationsController < Devise::RegistrationsController # 第一步:展示仅含邮箱的表单 def new @user = User.new end # 第一步提交邮箱的处理逻辑 def create @user = User.find_or_initialize_by(email: sign_up_params[:email]) # 校验邮箱是否已完成注册 if @user.persisted? && @user.encrypted_password.present? flash[:alert] = "该邮箱已注册,请直接登录" return redirect_to new_user_session_path end # 校验邮箱格式后发送确认邮件 if @user.valid? @user.generate_registration_token! UserMailer.registration_confirmation(@user).deliver_later flash[:notice] = "注册确认邮件已发送至你的邮箱,请点击邮件内链接完成后续注册" redirect_to root_path else render :new end end # 第二步:展示完整注册表单 def complete_registration @user = User.find_by(registration_token: params[:token]) if @user.nil? || !@user.valid_registration_token? flash[:alert] = "注册链接无效或已过期" redirect_to new_user_registration_path end end # 第二步提交完整注册信息的处理逻辑 def update_registration @user = User.find_by(registration_token: params[:token]) if @user.nil? || !@user.valid_registration_token? flash[:alert] = "注册链接无效或已过期" return redirect_to new_user_registration_path end if @user.update(user_params) # 标记邮箱已确认,无需二次验证 @user.confirm # 销毁已使用的token,避免重复提交 @user.update(registration_token: nil, registration_token_expires_at: nil) flash[:notice] = "注册成功,请登录" redirect_to new_user_session_path else render :complete_registration end end private # 第一步仅允许提交邮箱 def sign_up_params params.require(:user).permit(:email) end # 第二步允许提交的字段,可按需新增昵称、手机号等 def user_params params.require(:user).permit(:password, :password_confirmation, :name) end end
第三步:实现注册确认邮件逻辑
- 生成邮件发送器:
rails g mailer UserMailer registration_confirmation
- 修改
app/mailers/user_mailer.rb配置:
class UserMailer < ApplicationMailer # 替换为你的官方发件邮箱 default from: 'admin@yourdomain.com' def registration_confirmation(user) @user = user @registration_link = complete_registration_url(token: user.registration_token) mail(to: @user.email, subject: '请完成你的账号注册') end end
- 编写邮件模板
app/views/user_mailer/registration_confirmation.html.erb:
<h1>欢迎注册</h1> <p>请点击下方链接完成注册,链接24小时内有效:</p> <%= link_to '完成注册', @registration_link %>
第四步:编写视图文件
- 第一步邮箱提交页
app/views/users/registrations/new.html.erb:
<h2>注册账号</h2> <%= form_with model: @user, url: user_registration_path do |f| %> <% if @user.errors.any? %> <div id="error_explanation"> <ul> <% @user.errors.each do |error| %> <li><%= error.full_message %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :email, "邮箱" %><br /> <%= f.email_field :email, autofocus: true, autocomplete: "email" %> </div> <div class="actions"> <%= f.submit "提交" %> </div> <% end %>
- 第二步完整注册页
app/views/users/registrations/complete_registration.html.erb:
<h2>完成注册</h2> <%= form_with model: @user, url: update_registration_path(token: @user.registration_token), method: :put do |f| %> <% if @user.errors.any? %> <div id="error_explanation"> <ul> <% @user.errors.each do |error| %> <li><%= error.full_message %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :email, "邮箱" %><br /> <%= f.email_field :email, value: @user.email, readonly: true %> </div> <div class="field"> <%= f.label :password, "密码" %><br /> <%= f.password_field :password, autocomplete: "new-password" %> </div> <div class="field"> <%= f.label :password_confirmation, "确认密码" %><br /> <%= f.password_field :password_confirmation, autocomplete: "new-password" %> </div> <!-- 此处可新增你需要的其他注册字段 --> <div class="actions"> <%= f.submit "完成注册" %> </div> <% end %>
注意事项
- 生产环境需要先配置好邮件发送服务,避免确认邮件被识别为垃圾邮件
- 可根据业务需求调整注册token的有效时长,以及增加更多注册字段校验
内容的提问来源于stack exchange,提问作者Hà Mai
相关产品推荐
相关产品推荐

