邮件
本指南介绍如何从 AdonisJS 应用发送邮件。你将学习如何:
- 为 SMTP、Resend、Postmark、Mailgun、SparkPost、Brevo 和 SES 等服务配置邮件传输方式
- 使用流畅的 Message API 发送邮件
- 将邮件加入队列以进行后台投递
- 将邮件组织为可复用的邮件类
- 添加附件、内嵌图片以及日历邀请
- 使用 fake mailer 测试邮件功能
概述
@adonisjs/mail 包提供了一个统一的 API,用于通过各种提供者发送邮件。它构建于
Nodemailer
之上,增加了流畅的配置 API、将邮件组织为类的支持、丰富的测试 API,以及通过 messenger 进行后台邮件投递。
该包引入了两个关键概念。传输方式(transport) 是底层的投递机制(SMTP 服务器、Resend 或 Mailgun 之类的 API 服务)。mailer 是某个传输方式的已配置实例,你用它来发送邮件。你可以在应用中配置多个 mailer,每个 mailer 可以使用不同的传输方式,或相同的传输方式配合不同的设置,并可在运行时切换。
安装
使用以下命令安装并配置该包:
node ace add @adonisjs/mail
你可以在安装时预先选择传输方式:
node ace add @adonisjs/mail --transports=resend --transports=smtp
查看 add 命令执行的步骤
-
使用检测到的包管理器安装
@adonisjs/mail包。 -
在
adonisrc.ts文件中注册以下服务提供者与命令。adonisrc.ts{ commands: [ // ...other commands () => import('@adonisjs/mail/commands') ], providers: [ // ...other providers () => import('@adonisjs/mail/mail_provider') ] } -
创建
config/mail.ts文件。 -
为所选邮件服务定义环境变量及其校验。
配置
邮件配置位于 config/mail.ts。该文件定义了你的 mailer、默认发件人地址以及传输方式设置。
另见: 配置桩文件
import env from '#start/env'
import { defineConfig, transports } from '@adonisjs/mail'
const mailConfig = defineConfig({
/**
* 未指定 mailer 时使用的 mailer
*/
default: 'smtp',
/**
* 未在各封邮件上设置时使用的全局「from」地址
*/
from: {
address: '[email protected]',
name: 'My App',
},
/**
* 未在各封邮件上设置时使用的全局「reply-to」地址
*/
replyTo: {
address: '[email protected]',
name: 'My App Support',
},
/**
* 配置一个或多个 mailer。每个 mailer 使用一种传输方式,
* 并可以有自己的设置。
*/
mailers: {
smtp: transports.smtp({
host: env.get('SMTP_HOST'),
port: env.get('SMTP_PORT'),
}),
resend: transports.resend({
key: env.get('RESEND_API_KEY'),
baseUrl: 'https://api.resend.com',
}),
},
})
export default mailConfig
| 选项 | 说明 |
|---|---|
default | 在未指定 mailer 而调用 mail.send() 时使用的 mailer。 |
from | 全局发件人地址。除非在各封邮件上覆盖,否则都会使用它。 |
replyTo | 全局回复地址。除非在各封邮件上覆盖,否则都会使用它。 |
mailers | 一个包含你已配置 mailer 的对象。每个键是 mailer 名称,每个值是一个传输方式配置。 |
传输方式配置
每种传输方式都接受提供者特定的选项。请在下方切换器中选择一个提供者以进行配置。
transports.smtp
任何标准 SMTP 服务器。
安装带有 SMTP 传输方式的包。
node ace add @adonisjs/mail --transports=smtp
或在已有的配置中注册 mailer 与环境变量。
{
mailers: {
smtp: transports.smtp({
host: env.get('SMTP_HOST'),
port: env.get('SMTP_PORT'),
secure: false,
auth: {
type: 'login',
user: env.get('SMTP_USERNAME'),
pass: env.get('SMTP_PASSWORD'),
},
tls: {},
ignoreTLS: false,
requireTLS: false,
pool: false,
maxConnections: 5,
maxMessages: 100,
}),
},
}
SMTP_HOST=
SMTP_PORT=
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP 选项会直接转发给 Nodemailer。另见: Nodemailer SMTP 文档
transports.resend
面向开发者的现代邮件 API。
安装带有 Resend 传输方式的包。
node ace add @adonisjs/mail --transports=resend
或在已有的配置中注册 mailer 与环境变量。
{
mailers: {
resend: transports.resend({
key: env.get('RESEND_API_KEY'),
baseUrl: 'https://api.resend.com',
/**
* 可选:可以在运行时覆盖
*/
tags: [
{
name: 'category',
value: 'confirm_email',
},
],
}),
},
}
RESEND_API_KEY=
配置选项会发送到 Resend 的
/emails
API 端点。
transports.postmark
可靠的事务性邮件投递。
Postmark 不属于 node ace add 脚手架的一部分,因此需要手动注册 mailer 及其环境变量。
{
mailers: {
postmark: transports.postmark({
key: env.get('POSTMARK_API_KEY'),
baseUrl: 'https://api.postmarkapp.com',
/**
* 可选:可以在运行时覆盖
*/
messageStream: 'outbound',
tag: 'welcome',
trackOpens: true,
trackLinks: 'HtmlAndText',
metadata: {
userId: '1',
},
}),
},
}
POSTMARK_API_KEY=
配置选项会发送到 Postmark 的
/email
API 端点。
transports.mailgun
强大的邮件自动化与 API。
安装带有 Mailgun 传输方式的包。
node ace add @adonisjs/mail --transports=mailgun
或在已有的配置中注册 mailer 与环境变量。
{
mailers: {
mailgun: transports.mailgun({
key: env.get('MAILGUN_API_KEY'),
domain: env.get('MAILGUN_DOMAIN'),
baseUrl: 'https://api.mailgun.net/v3',
/**
* 可选:可以在运行时覆盖
*/
oDkim: true,
oTags: ['transactional', 'adonisjs_app'],
oDeliverytime: new Date(2024, 8, 18),
oTestMode: false,
oTracking: false,
oTrackingClick: false,
oTrackingOpens: false,
headers: {},
variables: {
appId: '',
userId: '',
},
}),
},
}
MAILGUN_API_KEY=
MAILGUN_DOMAIN=
配置选项会发送到 Mailgun 的
/messages.mime
API 端点。
transports.sparkpost
大容量、可预测的邮件投递。
安装带有 SparkPost 传输方式的包。
node ace add @adonisjs/mail --transports=sparkpost
或在已有的配置中注册 mailer 与环境变量。
{
mailers: {
sparkpost: transports.sparkpost({
key: env.get('SPARKPOST_API_KEY'),
baseUrl: 'https://api.sparkpost.com/api/v1',
/**
* 可选:可以在运行时覆盖
*/
startTime: new Date(),
openTracking: false,
clickTracking: false,
initialOpen: false,
transactional: true,
sandbox: false,
skipSuppression: false,
ipPool: '',
}),
},
}
SPARKPOST_API_KEY=
配置选项会发送到 SparkPost 的
/transmissions
API 端点。
transports.ses
构建于 AWS 之上的可扩展、低成本邮件服务。
安装带有 SES 传输方式的包,然后安装 AWS SDK。
node ace add @adonisjs/mail --transports=ses
npm i @aws-sdk/client-sesv2
或在已有的配置中注册 mailer 与环境变量。
{
mailers: {
ses: transports.ses({
apiVersion: '2010-12-01',
region: env.get('AWS_REGION'),
credentials: {
accessKeyId: env.get('AWS_ACCESS_KEY_ID'),
secretAccessKey: env.get('AWS_SECRET_ACCESS_KEY'),
},
/**
* Nodemailer 特定的选项
*/
sendingRate: 10,
maxConnections: 5,
}),
},
}
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=us-east-1
SES 选项会转发给 Nodemailer。另见: Nodemailer SES 文档
transports.brevo
邮件与营销平台(前身为 Sendinblue)。
安装带有 Brevo 传输方式的包。
node ace add @adonisjs/mail --transports=brevo
或在已有的配置中注册 mailer 与环境变量。
{
mailers: {
brevo: transports.brevo({
key: env.get('BREVO_API_KEY'),
baseUrl: 'https://api.brevo.com/v3',
/**
* 可选:可以在运行时覆盖
*/
tags: ['transactional', 'adonisjs_app'],
scheduledAt: new Date(2024, 8, 18),
}),
},
}
BREVO_API_KEY=
配置选项会发送到 Brevo 的
/smtp/email
API 端点。
发送你的第一封邮件
导入 mail 服务并调用 send 方法。该回调会接收一个 Message 实例用于配置邮件。
import type { HttpContext } from '@adonisjs/core/http'
import mail from '@adonisjs/mail/services/main'
import User from '#models/user'
export default class UsersController {
async store({ request }: HttpContext) {
const user = await User.create(request.all())
/**
* 创建用户后发送一封欢迎邮件。
* 回调负责配置邮件属性。
*/
await mail.send((message) => {
message
.to(user.email)
.from('[email protected]')
.subject('Welcome to our app!')
.htmlView('emails/welcome', { user })
})
return user
}
}
mail.send 方法会立即使用默认 mailer 投递邮件。对于高流量的生产应用,你应该将邮件加入队列以进行后台投递。
配置邮件
Message 类提供了一个流畅的 API 来构建邮件。在传入 mail.send 或 mail.sendLater 的回调中,你会收到一个该类的实例。
主题与发件人
await mail.send((message) => {
message
.subject('Your order has shipped')
.from('[email protected]')
})
from 方法接受字符串,或包含 address 与 name 的对象:
await mail.send((message) => {
message.from({
address: '[email protected]',
name: 'Acme Store'
})
})
收件人
使用 to、cc 和 bcc 来设置收件人。每个方法都接受字符串、对象或数组。
await mail.send((message) => {
message
.to(user.email)
.cc(user.manager.email)
.bcc('[email protected]')
})
await mail.send((message) => {
message
.to({
address: user.email,
name: user.fullName,
})
.cc([
{ address: '[email protected]', name: 'Team Lead' },
{ address: '[email protected]', name: 'Project Manager' }
])
})
使用 replyTo 方法设置回复地址:
await mail.send((message) => {
message
.from('[email protected]')
.replyTo('[email protected]')
})
邮件内容
使用 html 和 text 方法定义内联的 HTML 与纯文本内容,或使用 htmlView 和 textView 渲染 Edge 模板。
await mail.send((message) => {
/**
* 内联内容适用于简单邮件
*/
message.html(`
<h1>Reset your password</h1>
<p>Click <a href="${resetUrl}">here</a> to reset your password.</p>
`)
message.text(`
Reset your password
Visit ${resetUrl} to reset your password.
`)
})
对于大多数邮件,Edge 模板能提供更好的组织方式:
node ace make:view emails/password_reset_html
node ace make:view emails/password_reset_text
await mail.send((message) => {
/**
* 渲染 Edge 模板,将数据作为第二个参数传入
*/
message.htmlView('emails/password_reset_html', { user, resetUrl })
message.textView('emails/password_reset_text', { user, resetUrl })
})
另见: 配置 Edge
使用 MJML 实现响应式邮件
MJML
是一种可编译为响应式 HTML 邮件标记的语言。安装该包并使用 @mjml Edge 标签。
npm i mjml
@mjml()
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text font-size="20px" color="#333">
Welcome, {{ user.name }}!
</mj-text>
<mj-button href="{{ verifyUrl }}">
Verify Email
</mj-button>
</mj-column>
</mj-section>
</mj-body>
</mjml>
@end
将 MJML 选项 作为 props 传入标签:
@mjml({
keepComments: false,
fonts: {
Lato: 'https://fonts.googleapis.com/css?family=Lato:400,500,700'
}
})
{{-- MJML 内容 --}}
@end
将邮件加入队列
同步发送邮件会阻塞请求处理,直到等待邮件提供者的响应返回。为了获得更好的性能,请使用 mail.sendLater 将邮件加入队列以进行后台投递。
import mail from '@adonisjs/mail/services/main'
export default class UsersController {
async store({ request }: HttpContext) {
const user = await User.create(request.all())
/**
* 将邮件加入队列,而非立即发送。
* 请求无需等待投递即可完成。
*/
await mail.sendLater((message) => {
message
.to(user.email)
.from('[email protected]')
.subject('Welcome!')
.htmlView('emails/welcome', { user })
})
return user
}
}
默认情况下,邮件 messenger 使用内存队列。这意味着如果在进程终止前邮件尚未发送,它们就会丢失。
内存 messenger 适用于开发环境,但不推荐用于生产环境。如果你的应用在仍有待发邮件的情况下崩溃或重启,这些邮件将永远无法送达。请为生产部署使用像 BullMQ 这样的持久化队列。
使用 BullMQ 实现持久化队列
对于生产应用,请使用 BullMQ 和 Redis 配置一个持久化队列。这能确保邮件在进程重启后不会丢失。
npm i bullmq
创建一个自定义 messenger,将邮件任务存储到 Redis 中:
import { Queue } from 'bullmq'
import mail from '@adonisjs/mail/services/main'
const emailsQueue = new Queue('emails')
mail.setMessenger((mailer) => {
return {
async queue(mailMessage, config) {
/**
* 存储已编译的消息、配置和 mailer 名称。
* 一个 worker 进程会取走它并发送。
*/
await emailsQueue.add('send_email', {
mailMessage,
config,
mailerName: mailer.name,
})
}
}
})
创建一个 worker 来处理队列。在单独的进程中运行它:
import { Worker } from 'bullmq'
import mail from '@adonisjs/mail/services/main'
new Worker('emails', async (job) => {
if (job.name === 'send_email') {
const { mailMessage, config, mailerName } = job.data
/**
* 使用指定的 mailer 发送预编译的消息
*/
await mail.use(mailerName).sendCompiled(mailMessage, config)
}
})
在 mailer 之间切换
使用 mail.use() 通过特定的 mailer(而非默认 mailer)发送邮件。
import mail from '@adonisjs/mail/services/main'
/**
* 通过默认 mailer 发送事务性邮件
*/
await mail.send((message) => {
message.subject('Order confirmed')
})
/**
* 通过 Mailgun 发送营销邮件
*/
await mail.use('mailgun').send((message) => {
message.subject('Weekly newsletter')
})
/**
* 通过特定 mailer 将邮件加入队列
*/
await mail.use('resend').sendLater((message) => {
message.subject('Your report is ready')
})
mailer 实例会在进程生命周期内被缓存。要关闭连接并将其从缓存中移除,请使用 mail.close():
import mail from '@adonisjs/mail/services/main'
/**
* 关闭 mailgun 连接并从缓存中移除
*/
await mail.close('mailgun')
/**
* 下一次调用会创建全新的实例
*/
mail.use('mailgun')
附件
文件附件
使用 attach 方法并传入绝对文件路径来附加文件:
import app from '@adonisjs/core/services/app'
await mail.send((message) => {
message.attach(app.makePath('storage/invoices/inv-001.pdf'))
})
自定义附件文件名与其他选项:
await mail.send((message) => {
message.attach(app.makePath('storage/invoices/inv-001.pdf'), {
filename: 'invoice-october-2024.pdf',
contentType: 'application/pdf',
})
})
| 选项 | 说明 |
|---|---|
filename | 附件的显示名称。默认为文件的基本名。 |
contentType | MIME 类型。如果未设置,则从扩展名推断。 |
contentDisposition | 可以是 attachment(默认)或 inline。 |
headers | 作为键值对的自定义请求头。 |
来自流与缓冲区的附件
使用 attachData 附加来自流或缓冲区的动态内容:
import fs from 'node:fs'
await mail.send((message) => {
/**
* 从可读流附加
*/
message.attachData(fs.createReadStream('./report.pdf'), {
filename: 'report.pdf'
})
/**
* 从缓冲区附加
*/
message.attachData(Buffer.from('Hello world'), {
filename: 'greeting.txt',
encoding: 'utf-8',
})
})
不要将 attachData 与 mail.sendLater 一起使用。队列中的邮件会被序列化为 JSON,因此流无法存储,而缓冲区会显著增加存储体积。试图将带有流附件的邮件加入队列会失败。相反,请先将文件保存到磁盘,然后使用 attach 配合文件路径。
内嵌图片
在你的 Edge 模板中使用 embedImage 辅助函数将图片直接内嵌到 HTML 内容中。它使用 CID(Content-ID)附件,能在各种邮件客户端中可靠显示。
<img src="{{ embedImage(app.makePath('assets/logo.png')) }}" alt="Logo" />
该辅助函数返回一个 cid: URL,并会自动将图片作为附件添加。
对于动态的图片数据,请使用 embedImageData:
<img src="{{ embedImageData(chartBuffer, { filename: 'chart.png' }) }}" />
日历邀请
使用 icalEvent 方法附加日历事件。你可以提供原始的 iCalendar 内容,或使用流畅的 API。
import { DateTime } from 'luxon'
await mail.send((message) => {
message.icalEvent((calendar) => {
calendar.createEvent({
summary: 'Project kickoff meeting',
start: DateTime.now().plus({ days: 1 }).set({ hour: 10 }),
end: DateTime.now().plus({ days: 1 }).set({ hour: 11 }),
location: 'Conference Room A',
})
}, {
method: 'REQUEST',
filename: 'meeting.ics',
})
})
日历对象是一个 ical-generator 的实例。
从文件或 URL 加载邀请内容:
import app from '@adonisjs/core/services/app'
await mail.send((message) => {
/**
* 来自本地文件
*/
message.icalEventFromFile(
app.resourcesPath('invites/standup.ics'),
{ method: 'REQUEST', filename: 'standup.ics' }
)
/**
* 来自 URL
*/
message.icalEventFromUrl(
'https://example.com/calendar/event-123.ics',
{ method: 'REQUEST', filename: 'event.ics' }
)
})
自定义请求头
使用 header 方法添加自定义请求头:
await mail.send((message) => {
message.header('X-Entity-Ref', 'order-12345')
message.header('X-Priority', '1')
})
对于不应被编码或折行的请求头,请使用 preparedHeader:
await mail.send((message) => {
message.preparedHeader(
'X-Custom-Data',
'value with special chars or very long content'
)
})
列表请求头
辅助方法简化了用于邮件列表功能的常见 List-* 请求头:
await mail.send((message) => {
message.listUnsubscribe({
url: 'https://example.com/unsubscribe?token=abc',
comment: 'Unsubscribe from this list'
})
message.listHelp('[email protected]?subject=help')
/**
* 对于其他 List-* 请求头
*/
message.addListHeader('post', 'https://example.com/list/post')
})
Gmail 和 Yahoo 等邮箱提供者要求批量发件人支持
一键退订(RFC 8058)
。它允许提供者在收到单个 HTTP POST 请求发往你的退订 URL 时退订收件人,因此收件人无需离开收件箱或加载确认页面。
通过向 listUnsubscribe 传入 { oneClick: true } 作为第二个参数来启用它。除了你的 List-Unsubscribe URL,AdonisJS 还会发出 List-Unsubscribe-Post: List-Unsubscribe=One-Click 请求头,以表明支持一键退订。你传入的 URL 必须是一个接受 POST 请求并立即退订收件人的 https 端点。
await mail.send((message) => {
message.listUnsubscribe('https://example.com/unsubscribe?token=abc', {
oneClick: true
})
})
基于类的邮件
对于复杂的应用,请将邮件组织为专门的类,而非使用内联回调。这能提升可测试性,并使控制器专注于请求处理。
node ace make:mail verify_email
import User from '#models/user'
import router from '@adonisjs/core/services/router'
import { BaseMail } from '@adonisjs/mail'
export default class VerifyEmailNotification extends BaseMail {
/**
* 为常见邮件选项设置类属性
*/
from = '[email protected]'
subject = 'Please verify your email address'
constructor(private user: User) {
super()
}
/**
* 在 prepare 方法中配置邮件。
* 会在发送前自动调用。
*/
prepare() {
const verifyUrl = router.makeUrl('email.verify', {
token: this.user.verificationToken
})
this.message
.to(this.user.email)
.htmlView('emails/verify_email', {
user: this.user,
verifyUrl,
})
}
}
通过将一个实例传入 mail.send 或 mail.sendLater 来发送邮件:
import mail from '@adonisjs/mail/services/main'
import VerifyEmailNotification from '#mails/verify_email'
export default class AuthController {
async register({ request }: HttpContext) {
const user = await User.create(request.all())
await mail.sendLater(new VerifyEmailNotification(user))
return { message: 'Check your email to verify your account' }
}
}
| 属性/方法 | 说明 |
|---|---|
from | 默认发件人地址。在 prepare 中用 message.from() 覆盖。 |
subject | 默认主题行。在 prepare 中用 message.subject() 覆盖。 |
replyTo | 默认回复地址。 |
prepare() | 配置邮件。在发送前自动调用。 |
build() | 继承自 BaseMail。调用 prepare 并编译邮件。 |
另见: Make mail 命令
测试
mail 包提供了一个 fake mailer,用于在不实际发送邮件的情况下测试邮件功能。
使用 fake mailer
调用 mail.fake() 来拦截所有邮件。返回的对象包含一个 mails 属性用于断言。
import { test } from '@japa/runner'
import mail from '@adonisjs/mail/services/main'
import VerifyEmailNotification from '#mails/verify_email'
test.group('Users | register', () => {
test('sends verification email on registration', async ({ client }) => {
/**
* Fake 该 mailer。`using` 关键字会在测试结束时
* 自动恢复真实的 mailer。
*/
using fake = mail.fake()
await client
.post('/register')
.form({ email: '[email protected]', password: 'secret123' })
/**
* 断言邮件已被发送
*/
fake.mails.assertSent(VerifyEmailNotification, ({ message }) => {
return message
.hasTo('[email protected]')
.hasSubject('Please verify your email address')
})
})
test('does not send password reset when user not found', async ({ client }) => {
using fake = mail.fake()
await client
.post('/forgot-password')
.form({ email: '[email protected]' })
fake.mails.assertNotSent(PasswordResetNotification)
})
})
如果你需要更精细地控制真实 mailer 何时恢复,也可以手动调用 mail.restore()。
断言方法
mails 对象提供了以下断言方法:
| 方法 | 说明 |
|---|---|
assertSent(Mail, finder?) | 断言某个邮件类已被发送。可选的 finder 回调用于额外检查。 |
assertNotSent(Mail, finder?) | 断言某个邮件类未被发送。 |
assertSentCount(count) | 断言发送的邮件总数。 |
assertSentCount(Mail, count) | 断言某个特定类发送的邮件数量。 |
assertNoneSent() | 断言没有发送任何邮件。 |
assertQueued(Mail, finder?) | 断言某个邮件已通过 sendLater 加入队列。 |
assertNotQueued(Mail, finder?) | 断言某个邮件未被加入队列。 |
assertQueuedCount(count) | 断言加入队列的邮件总数。 |
assertQueuedCount(Mail, count) | 断言某个特定类加入队列的邮件数量。 |
assertNoneQueued() | 断言没有邮件被加入队列。 |
直接测试邮件类
通过构建邮件类(而不发送)来单独测试它们:
import { test } from '@japa/runner'
import { UserFactory } from '#database/factories/user_factory'
import VerifyEmailNotification from '#mails/verify_email'
test.group('VerifyEmailNotification', () => {
test('builds correct message', async () => {
const user = await UserFactory.create()
const email = new VerifyEmailNotification(user)
/**
* 构建邮件并渲染模板
*/
await email.buildWithContents()
/**
* 断言邮件属性
*/
email.message.assertTo(user.email)
email.message.assertFrom('[email protected]')
email.message.assertSubject('Please verify your email address')
/**
* 断言渲染后的内容
*/
email.message.assertHtmlIncludes(`Hello ${user.name}`)
email.message.assertHtmlIncludes('/verify/')
})
})
访问已发送的邮件
检索已发送或已加入队列的邮件列表以进行自定义断言:
using fake = mail.fake()
/**
* 获取所有已发送的邮件
*/
const sentEmails = fake.mails.sent()
/**
* 获取所有已加入队列的邮件
*/
const queuedEmails = fake.mails.queued()
/**
* 查找特定的邮件
*/
const verifyEmail = sentEmails.find((email) => {
return email instanceof VerifyEmailNotification
})
if (verifyEmail) {
verifyEmail.message.assertHtmlIncludes('Verify your email')
}
自定义传输方式
创建自定义传输方式来集成包中未包含的邮件提供者。传输方式会封装一个 Nodemailer 传输并对其响应进行归一化。
import nodemailer from 'nodemailer'
import mandrillTransport from 'nodemailer-mandrill-transport'
import { MailResponse } from '@adonisjs/mail'
import type { NodeMailerMessage, MailTransportContract } from '@adonisjs/mail/types'
export type MandrillConfig = {
auth: {
apiKey: string
}
}
export class MandrillTransport implements MailTransportContract {
#config: MandrillConfig
constructor(config: MandrillConfig) {
this.#config = config
}
async send(
message: NodeMailerMessage,
config?: MandrillConfig
): Promise<MailResponse> {
/**
* 使用合并后的配置创建 nodemailer 传输
*/
const transporter = nodemailer.createTransport(
mandrillTransport({ ...this.#config, ...config })
)
const response = await transporter.sendMail(message)
/**
* 返回归一化后的响应
*/
return new MailResponse(response.messageId, response.envelope, response)
}
}
为在配置文件中使用而创建一个工厂函数:
import type { MailManagerTransportFactory } from '@adonisjs/mail/types'
export function mandrillTransport(
config: MandrillConfig
): MailManagerTransportFactory {
return () => new MandrillTransport(config)
}
在配置中注册传输方式:
import env from '#start/env'
import { defineConfig } from '@adonisjs/mail'
import { mandrillTransport } from '#app/mail/transports/mandrill'
const mailConfig = defineConfig({
mailers: {
mandrill: mandrillTransport({
auth: {
apiKey: env.get('MANDRILL_API_KEY'),
},
}),
},
})
自定义模板引擎
默认情况下,mail 包使用 Edge 来渲染邮件模板。要使用不同的模板引擎,请覆盖 Message 类上的静态 templateEngine 属性。
import { Message } from '@adonisjs/mail'
Message.templateEngine = {
async render(templatePath, data) {
/**
* 使用你偏好的模板引擎
*/
return myTemplateEngine.render(templatePath, data)
}
}
事件
mail 包在邮件生命周期中会发出事件。
另见: 事件参考