You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rails邮件类(Mailer)的before_action中获取当前用户以实现统一邮件发送权限校验

Solution for Blocking Mail Deliveries in User Mailer Without Repeating Checks

Got it, let's work through this. The core issue here is that current_user isn't accessible in Action Mailers—they operate completely outside the request cycle, so controller-specific helpers like current_user don't exist there. But we can implement your check in a reusable way without adding code to every single mail method. Here's how to do it:

Step 1: Pass the User Instance to Every Mailer Method

First, whenever you trigger a mailer (whether in a controller, background job, or elsewhere), explicitly pass the user object as an argument:

# Example in a controller
UserMailer.welcome_email(current_user).deliver_later

To avoid repeating @user = user in every mailer method, we can add a helper before_action to extract the user from the method's arguments automatically:

class UserMailer < ApplicationMailer
  before_action :set_user
  before_action :check_if_users_can_receive_mailers

  # Your existing mail methods (now accepting a user argument)
  def welcome_email(user)
    mail(to: user.email, subject: "Welcome to Our Platform!")
  end

  def password_reset_email(user)
    mail(to: user.email, subject: "Reset Your Password")
  end

  private

  # Extract the user from the first argument of the mailer method
  def set_user
    @user = arguments.first
  end

  def check_if_users_can_receive_mailers
    # Disable delivery if the user shouldn't receive mailers
    unless @user&.send_mailers?
      self.message.perform_deliveries = false
      # Optional: Add a log entry for debugging
      Rails.logger.info "Skipped mail delivery for user #{@user.id} - send_mailers is set to false"
    end
  end
end

Step 2: How perform_deliveries Works

Setting self.message.perform_deliveries = false tells Action Mailer to skip sending the email entirely. This is cleaner than returning early because it lets all your mail setup code run (like template rendering) but aborts the actual delivery step.

Alternative: Halt Execution with an Exception (If Needed)

If you want to stop the mailer method entirely when the user can't receive mail, you can raise a custom exception and rescue it to log the issue:

def check_if_users_can_receive_mailers
  unless @user&.send_mailers?
    raise MailDeliveryBlocked, "User #{@user.id} cannot receive mailers"
  end
end

# Add a rescue handler in your mailer to catch the exception
rescue_from MailDeliveryBlocked do |e|
  Rails.logger.error e.message
end

But for most cases, using perform_deliveries = false is the simpler, more straightforward approach.

Quick Reminder: Why current_user Fails Here

Just to confirm what you already know: current_user is a controller helper that relies on the request's session to fetch the user. Since mailers can run outside of a request (like in background jobs), there's no active session to pull from—so explicitly passing the user instance is the correct pattern here.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 11:22:31