如何在Rails邮件类(Mailer)的before_action中获取当前用户以实现统一邮件发送权限校验
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

