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

Rails使用aws-sdk-ses对接AWS SES时如何解决邮件预览失效问题

Ruby on Rails 对接 AWS SES 后本地邮件预览失效问题

问题描述

我在开发Ruby on Rails应用时,选用AWS SES服务发送用户邮件,对接使用aws-sdk-ses gem,当前本地邮件预览功能无法正常使用。

相关代码

app/mailers/user_mailer.rb 实现如下:

# frozen_string_literal: true

class UserMailer < ApplicationMailer
  layout 'mailer'

  def invite
    invitation = params[:invitation]
    email = invitation.email
    subject = 'Invitation email'

    # The HTML body of the email
    htmlbody = render_to_string(
      template: 'user_mailer/invite',
      layout: true,
      locals: {
        email: receiver_email,
      }
    )
    send_email(
       receiver: receiver_email,
       sender: 'default@email.com',
       subject: subject,
       body: htmlbody
    )
  end

  def send_email(receiver:, sender:, subject:, body:)
    AWS_SES_CLIENT.send_email(
      {
        destination: { to_addresses: [receiver] },
        message: {
          body: {
            html: { charset: 'UTF-8',
                    data: body }
          },
          subject: {
            charset: 'UTF-8',
            data: subject
          }
        },
        source: sender
      }
    )
  rescue Aws::SES::Errors::ServiceError => e
    Rails.logger.info "ERROR! Email for #{receiver} with subject #{subject} wasn't sent with error message: #{e}"
  end
end

spec/mailers/preview/user_mailer_preview.rb 预览类实现如下:

# frozen_string_literal: true

# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview
  def invite
    UserMailer.with(invitation: UserInvitation.first).invite
  end
end

报错信息

访问预览地址http://localhost:3000/rails/mailers/user_mailer时,页面返回如下提示:

You are trying to preview an email that does not have any content.
This is probably because the mail method has not been called in
user_mailer#invite.

初步排查

我推测Rails原生邮件预览器的实现逻辑是追踪mail方法的调用,而我当前通过自定义的send_email方法直接调用AWS SES接口发信,未调用ActionMailer原生的mail方法,因此预览功能无法正常工作。查阅AWS SDK官方指南未找到对应解决方案,想确认是否可以通过调整action_mailer的相关配置解决该问题。


问题原因

你的判断完全正确:Rails 原生邮件预览的工作逻辑是捕获 mailer 动作返回的 Mail::Message 对象、读取其内容渲染页面,只有调用 ActionMailer 内置的 mail 方法才会生成这个对象。你当前直接在 mailer 里调用 AWS SES SDK 发信,动作最终返回的是 SES 接口响应值(异常捕获后为nil),预览器拿不到邮件内容,就会抛出上述报错。

不建议在 mailer 中硬编码 SES 客户端调用逻辑,ActionMailer 原生支持 SES 作为投递适配器,不需要手动对接SDK,同时可以完美兼容预览、测试环境邮件拦截、批量投递配置等所有内置功能。

修复方案

    1. 移除 mailer 中自定义的 send_email 方法,删除手动实例化 AWS_SES_CLIENT 发信的逻辑
    1. 配置 ActionMailer 投递方式
      生产环境在 config/environments/production.rb 中添加配置:
    config.action_mailer.delivery_method = :ses
    
    本地开发环境不需要额外修改投递配置,默认的 :test 或常用的 :letter_opener 配置都不会影响预览功能。SES 投递适配器会自动读取本地已配置的 AWS 凭证,和你之前使用 aws-sdk-ses 的凭证逻辑完全一致,不需要额外调整。
    1. 重构 mailer 方法为 ActionMailer 标准写法,移除手动 render_to_string 的逻辑,直接用内置 mail 方法构造邮件对象,模板变量用实例变量传递即可:
    # frozen_string_literal: true
    
    class UserMailer < ApplicationMailer
      layout 'mailer'
    
      def invite
        invitation = params[:invitation]
        @receiver_email = invitation.email
        mail(
          to: @receiver_email,
          from: 'default@email.com',
          subject: 'Invitation email'
        )
      end
    end
    
    注意原代码存在笔误:局部变量 email 赋值后未使用,后续调用的是未定义的 receiver_email,重构后直接用实例变量传递给模板即可,不需要手动渲染模板字符串。
    1. 保持现有预览类代码不变,重启本地服务后再访问预览地址即可正常展示邮件内容。

特殊场景兼容(不推荐)

如果有特殊原因必须保留手动调用 SES SDK 的逻辑,可以在方法中判断当前运行环境:预览/开发环境调用原生 mail 方法返回 Mail::Message 对象供预览使用,生产环境再走手动 SES 调用逻辑。但这种写法会重复维护邮件构造逻辑,后续改动容易出现多环境逻辑不一致的问题,优先使用官方适配器方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 18:01:08