Mailer Reference Guide

Mailer extension

若要使用邮件服务,您需要添加 `quarkus-mailer`扩展。

你可以使用以下方式将扩展添加到你的项目:

> ./mvnw quarkus:add-extensions -Dextensions="mailer"

或者只需将以下依赖项添加到你的项目中:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-mailer</artifactId>
</dependency>

Accessing the mailer

您可以使用以下方法在应用程序中注入发送器:

@Inject
Mailer mailer;

@Inject
ReactiveMailer reactiveMailer;

有 2 个 API:

  • `io.quarkus.mailer.Mailer`提供命令式(阻塞和同步)API;

  • `io.quarkus.mailer.reactive.ReactiveMailer`提供反应式(非阻塞和异步)API

这两个 API 在功能上是等效的。事实上,`Mailer`实现构建在 `ReactiveMailer`实现之上。

Deprecation

`io.quarkus.mailer.ReactiveMailer`已弃用,被 `io.quarkus.mailer.reactive.ReactiveMailer`所取代。

如要发送一封简单电子邮件,请按以下步骤操作:

// Imperative API:
mailer.send(Mail.withText("to@acme.org", "A simple email from quarkus", "This is my body."));
// Reactive API:
Uni<Void> stage = reactiveMailer.send(Mail.withText("to@acme.org", "A reactive email from quarkus", "This is my body."));

例如,您可以在 HTTP 端点中使用 `Mailer`如下所示:

@GET
@Path("/imperative")
public void sendASimpleEmail() {
    mailer.send(Mail.withText("to@acme.org", "A simple email from quarkus", "This is my body"));
}

@GET
@Path("/reactive")
public Uni<Void> sendASimpleEmailAsync() {
    return reactiveMailer.send(
            Mail.withText("to@acme.org", "A reactive email from quarkus", "This is my body"));
}

Creating Mail objects

发送器可让您发送 `io.quarkus.mailer.Mail`对象。您可以从构造函数或 `Mail.withText`和 `Mail.withHtml`帮助程序方法创建新 `io.quarkus.mailer.Mail`实例。`Mail`实例可让您添加收件人(to、cc 或 bcc)、设置主题、标头、发件人(from)地址……

您还可以一次发送多个 `Mail`对象:

mailer.send(mail1, mail2, mail3);

Sending attachments

要发送附件,只需在 `io.quarkus.mailer.Mail`实例上使用 `addAttachment`方法:

@GET
@Path("/attachment")
public void sendEmailWithAttachment() {
        mailer.send(Mail.withText("clement.escoffier@gmail.com", "An email from quarkus with attachment",
                "This is my body")
                .addAttachment("my-file-1.txt",
                        "content of my file".getBytes(), "text/plain")
                .addAttachment("my-file-2.txt",
                        new File("my-file.txt"), "text/plain")
        );
}

附件可以从原始字节(如代码段中所示)或文件创建。请注意,文件的解析是基于应用程序的工作目录。

Sending HTML emails with inlined attachments

发送 HTML 电子邮件时,您可以在其中添加内联附件。例如,您可以随电子邮件发送图片,图片将显示在邮件内容中。如果您将图片文件放入 `META-INF/resources`文件夹,您应该指定文件的完整路径,e.g.`META-INF/resources/quarkus-logo.png`否则,Quarkus 会在根目录中查找该文件。

@GET
@Path("/html")
public void sendingHTML() {
    String body = "<strong>Hello!</strong>" + "\n" +
        "<p>Here is an image for you: <img src=\"cid:my-image@quarkus.io\"/></p>" +
        "<p>Regards</p>";
    mailer.send(Mail.withHtml("to@acme.org", "An email in HTML", body)
        .addInlineAttachment("quarkus-logo.png",
            new File("quarkus-logo.png"),
            "image/png", "<my-image@quarkus.io>"));
}

请注意 _content-id_的格式和引用。根据规范,创建内联附件时,内容 ID 必须按以下格式构建: <id@domain>。如果您不将内容 ID 包含在 <>`之间,它会自动为您进行包装。当您想要引用附件时,比如在 `src`属性中,请使用 `cid:id@domain(不含 <`和 `>)。

Message Body Based on Qute Templates

还可以使用 Qute templates自动创建消息正文,从而发送电子邮件。

您可以在 Java 代码中定义类型安全邮件模板。只要用 `@io.quarkus.qute.CheckedTemplate`注释一个类,该类中的所有返回 `MailTemplate`的 `static native`方法都将用于定义类型安全邮件模板及它们需要的参数列表:

import io.quarkus.mailer.MailTemplate;
import io.quarkus.qute.CheckedTemplate;

@Path("")
public class MailingResource {

    @CheckedTemplate
    static class Templates {
        public static native MailTemplateInstance hello(String name); (1)
    }

    @GET
    @Path("/mail")
    public Uni<Void> send() {
        // the template looks like: Hello {name}! (2)
        return Templates.hello("John")
           .to("to@acme.org")  (3)
           .subject("Hello from Qute template")
           .send(); (4)
    }
}
1 按照惯例,可以使用封闭的类名和方法名来找到模板。在此特定情况下,我们将使用 `src/main/resources/templates/MailingResource/hello.html`和 `src/main/resources/templates/MailingResource/hello.txt`模板来创建消息正文。
2 设置用于模板的数据。
3 创建邮件模板实例并设置收件人。
4 `MailTemplate.send()`触发渲染,渲染完成后,通过 `Mailer`实例发送电子邮件。

或者,使用一个实现 `io.quarkus.mailer.MailTemplate`的 Java 记录。记录组件表示模板参数。

import io.quarkus.mailer.MailTemplate;
import io.quarkus.qute.CheckedTemplate;

@Path("")
public class MailingResource {

    record hello(String name) implements MailTemplateInstance { (1)
    }

    @GET
    @Path("/mail")
    public Uni<Void> send() {
        // the template looks like: Hello {name}! (2)
        return new hello("John")
           .to("to@acme.org")  (3)
           .subject("Hello from Qute template")
           .send(); (4)
    }
}
1 按照惯例,使用封闭的类名称和记录名称来查找模板。在此特殊情况下,我们将使用 `src/main/resources/templates/MailingResource/hello.html`和 `src/main/resources/templates/MailingResource/hello.txt`模板创建消息正文。
2 设置用于模板的数据。
3 创建邮件模板实例并设置收件人。
4 `MailTemplate.send()`触发渲染,渲染完成后,通过 `Mailer`实例发送电子邮件。

还可以不使用类型安全模板执行此操作:

import io.quarkus.mailer.MailTemplate;

@Inject
@Location("hello")
MailTemplate hello; (1)

@GET
@Path("/mail")
public Uni<Void> send() {
    return hello.to("to@acme.org") (2)
       .subject("Hello from Qute template")
       .data("name", "John") (3)
       .send() (4)
}
1 如果没有提供 `@Location`限定符,则使用字段名称来查找模板。否则,在指定的位置搜索模板。在此特殊情况下,我们将使用 `src/main/resources/templates/hello.html`和 `src/main/resources/templates/hello.txt`模板创建消息正文。
2 创建邮件模板实例并设置收件人。
3 设置用于模板的数据。
4 `MailTemplate.send()`触发渲染,渲染完成后,通过 `Mailer`实例发送电子邮件。

注入的邮件模板会在构建期间得到验证。如果在 `src/main/resources/templates`中没有匹配的模板,则构建将失败。

Execution model

反应型发送器是无阻塞的,结果将提供在一个 I/O 线程上。请参阅 Quarkus Reactive Architecture documentation以了解此主题的更多详细信息。

非反应型发送器会阻塞直到消息发送到 SMTP 服务器。请注意,这并不意味着消息已被发送,而只是将其成功发送到了 SMTP 服务器,SMTP 服务器将负责发送。

Testing email sending

由于在开发和测试过程中发送电子邮件非常不方便,因此可以将 quarkus.mailer.mock`布尔值配置设置为 `true,以防止实际发送电子邮件,而是将它们打印到 stdout 并将它们收集到 `MockMailbox`bean 中。如果您在 dev 或 test 模式下运行 Quarkus,则这是默认设置。

然后,可以编写测试以验证您的电子邮件是否已发送,例如,通过 REST 端点:

@QuarkusTest
class MailTest {

    private static final String TO = "foo@quarkus.io";

    @Inject
    MockMailbox mailbox;

    @BeforeEach
    void init() {
        mailbox.clear();
    }

    @Test
    void testTextMail() throws MessagingException, IOException {
        // call a REST endpoint that sends email
        given()
        .when()
        .get("/send-email")
        .then()
           .statusCode(202)
           .body(is("OK"));

        // verify that it was sent
        List<Mail> sent = mailbox.getMessagesSentTo(TO);
        assertThat(sent).hasSize(1);
        Mail actual = sent.get(0);
        assertThat(actual.getText()).contains("Wake up!");
        assertThat(actual.getSubject()).isEqualTo("Alarm!");

        assertThat(mailbox.getTotalMessagesSent()).isEqualTo(6);
    }
}

另一个选择是使用 Quarkus Mailpit扩展,该扩展为 Mailpit提供开发服务,这是一个用于测试和调试电子邮件发送的漂亮界面。

Using the underlying Vert.x Mail Client

Quarkus Mailer 是在 Vert.x Mail Client基础上实现的,它提供了一种异步和无阻塞的方式来发送电子邮件。如果您需要对发送邮件的方式进行精细控制,例如,如果您需要检索消息 ID,您可以注入底层客户端并直接使用它:

@Inject MailClient client;

公开了三种 API 风格:

  • the Mutiny client (io.vertx.mutiny.ext.mail.MailClient)

  • the bare client (io.vertx.ext.mail.MailClient)

查看 Using Vert.x guide以了解有关这些不同 API 以及如何选择最适合您的 API 的更多详细信息。

检索到的 `MailClient`使用上面介绍的配置属性进行配置。您还可以创建自己的实例并传递自己的配置。

Using SSL with native executables

请注意,如果您为发送器启用了 SSL 并且您要构建一个本机可执行文件,则您需要启用 SSL 支持。请参阅 Using SSL With Native Executables指南以获取更多信息。

Configuring the SMTP credentials

建议对任何敏感数据进行加密,例如 quarkus.mailer.password。一种方法是将值保存到一个安全的存储中,例如 HashiCorp Vault,并从配置中引用它。假设 Vault 中在路径 myapps/myapp/myconfig`包含键 `mail-password,那么邮件扩展可以简单地配置为:

...
# path within the kv secret engine where is located the application sensitive configuration
# This uses the https://github.com/quarkiverse/quarkus-vault extension.
quarkus.vault.secret-config-kv-path=myapps/myapp/myconfig

...
quarkus.mailer.password=${mail-password}

请注意,密码值仅在启动时评估一次。如果在 Vault 中更改了 mail-password,获取新值的唯一方法是重新启动应用程序。

请使用 Vault,您需要 Quarkus Vault扩展。可以在 extension documentation中找到有关此扩展及其配置的更多详细信息。

有关 Mailer 配置的更多信息,请参阅 Configuration Reference

Configuring TLS

SMTP 提供了多种使用 TLS 的方法:

  • StartTLS:客户端使用普通连接连接到服务器,然后升级到安全连接。

  • SSL/TLS:客户端从一开始就使用安全连接连接到服务器。

Configuring STARTTLS

要使用 STARTTLS,您需要将 start-tls 属性配置为 REQUIREDOPTIONAL,并将 tls 设置为 false

quarkus.mailer.tls=false
quarkus.mailer.start-tls=REQUIRED

tls 设置为 false 可确保我们使用纯连接进行连接,然后使用 STARTTLS 升级到安全连接。

要配置信任库,您可以使用存储在 TLS registry 中的 named TLS 配置:

quarkus.mailer.tls=false
quarkus.mailer.start-tls=REQUIRED
quarkus.mailer.tls-configuration-name=my-mailer # Reference the named configuration

quarkus.tls.my-mailer.trust-store.pem.certs=server-cert.pem # Configure the trust store

虽然不建议,但您可以通过将 quarkus.tls.trust-all 设为 true 来信任所有证书:

quarkus.mailer.tls=false
quarkus.mailer.start-tls=REQUIRED
quarkus.mailer.tls-configuration-name=my-mailer # Reference the named configuration
quarkus.tls.my-mailer.trust-all=true

其他方法是,可以使用 deprecated quarkus.mailer.trust-store.pathsquarkus.mailer.trust-all 属性:

quarkus.mailer.tls=false
quarkus.mailer.start-tls=REQUIRED
quarkus.mailer.truststore.paths=target/certs/mailpit-ca.crt
quarkus.mailer.tls=false
quarkus.mailer.start-tls=REQUIRED
quarkus.mailer.trust-all=true

要使用 START_TLS,请确保将 tls 设置为 false,并设置 start-tlsREQUIREDOPTIONAL

Configuring SSL/TLS

要建立一个 TLS 连接,您需要使用 TLS registry 配置一个 named 配置:

quarkus.tls.my-mailer.trust-store.p12.path=server-truststore.p12
quarkus.tls.my-mailer.trust-store.p12.password=secret

quarkus.mailer.tls-configuration-name=my-mailer # Reference the named configuration

在使用邮件发送器时,必须使用 named 配置以避免与其他 TLS 配置发生冲突。邮件发送器不会使用默认的 TLS 配置。

在配置 named TLS 配置时,TLS 默认启用。如果您的 SMTP 服务器使用的是有效的(受信任的)证书,因此不需要特定的 TLS 配置,则需要显式启用 TLS(因为您不必配置信任存储):

quarkus.mailer.tls=true

quarkus.tls.trust-all 设置为 true 时,信任存储配置会被忽略。这在生产环境中是不建议使用的。此外,我们建议您避免使用 quarkus.tls.trust-all,而是在需要使用 trust-all 时使用命名配置:

quarkus.tls.my-mailer.trust-all=true
quarkus.mailer.tls-configuration-name=my-mailer # Reference the named configuration

您还可以使用已弃用的 quarkus.mailer.trust-all=true 属性。

Multiple mailer configurations

某些应用程序需要通过不同的 SMTP 服务器发送邮件。

Quarkus 完全支持此用例,您还可以配置多个邮件程序:

quarkus.mailer.from=your-from-address@gmail.com 1
quarkus.mailer.host=smtp.gmail.com

quarkus.mailer.aws.from=your-from-address@gmail.com 2
quarkus.mailer.aws.host=${ses.smtp}
quarkus.mailer.aws.port=587

quarkus.mailer.sendgrid.from=your-from-address@gmail.com 3
quarkus.mailer.sendgrid.host=${sendgrid.smtp-host}
quarkus.mailer.sendgrid.port=465
1 默认邮件程序的配置。
2 名为 aws 的邮件程序配置。
3 一个名为 sendgrid 的邮件发送器的配置。

然后,使用 `@MailerName`CDI 限定符访问命名的邮件发送器:

@Inject 1
Mailer mailer;

@Inject 1
ReactiveMailer reactiveMailer;

@Inject 1
@Location("hello")
MailTemplate mailTemplate;

@Inject
@MailerName("aws") 2
Mailer mailer;

@Inject
@MailerName("aws") 2
ReactiveMailer reactiveMailer;

@Inject
@MailerName("aws") 2
@Location("hello")
MailTemplate mailTemplate;

@Inject
@MailerName("sendgrid") 3
Mailer mailer;

@Inject
@MailerName("sendgrid") 3
ReactiveMailer reactiveMailer;

@Inject
@MailerName("sendgrid") 3
@Location("hello")
MailTemplate mailTemplate;
1 为默认配置注入没有限定符的实例。
2 aws 配置注入具有 @MailerName("aws") 限定符的实例。
3 sendgrid 配置注入具有 @MailerName("sendgrid") 限定符的实例。

当前仅为默认邮件发送器配置支持使用 @CheckedTemplate 的安全类型模板。 对命名邮件发送器配置使用 MailTemplate 注入。

本节提供与常用邮件服务配对使用的配置。

Gmail specific configuration

如果你想使用 Gmail SMTP 服务器,首先在 Google Account > Security > App passwords 创建一个专门的密码或转到 [role="bare"][role="bare"]https://myaccount.google.com/apppasswords。

你需要在 [role="bare"][role="bare"]https://myaccount.google.com/security 开启两步验证,才能访问应用密码页面。

完成后,可以通过将以下属性添加到你的 application.properties 来配置 Quarkus 应用程序:

使用 STARTTLS

quarkus.mailer.auth-methods=DIGEST-MD5 CRAM-SHA256 CRAM-SHA1 CRAM-MD5 PLAIN LOGIN
quarkus.mailer.from=YOUREMAIL@gmail.com
quarkus.mailer.host=smtp.gmail.com
quarkus.mailer.port=587
quarkus.mailer.start-tls=REQUIRED
quarkus.mailer.username=YOUREMAIL@gmail.com
quarkus.mailer.password=YOURGENERATEDAPPLICATIONPASSWORD

quarkus.mailer.mock=false # In dev mode, prevent from using the mock SMTP server

或者使用 TLS/SSL:

quarkus.mailer.auth-methods=DIGEST-MD5 CRAM-SHA256 CRAM-SHA1 CRAM-MD5 PLAIN LOGIN
quarkus.mailer.from=YOUREMAIL@gmail.com
quarkus.mailer.host=smtp.gmail.com
quarkus.mailer.port=465
quarkus.mailer.tls=true
quarkus.mailer.username=YOUREMAIL@gmail.com
quarkus.mailer.password=YOURGENERATEDAPPLICATIONPASSWORD

quarkus.mailer.mock=false # In dev mode, prevent from using the mock SMTP server

Quarkus 邮件发送器需要 quarkus.mailer.auth-methods 配置选项才能支持使用 Gmail 进行密码验证。默认情况下,邮件发送器和 Gmail 都默认为 XOAUTH2,它要求注册应用程序、获取令牌等。

AWS SES - Simple Email Service

Prerequisites

  1. SES 身份检查,按照流程设置 DKIM 验证

  2. 从 [role="bare"][role="bare"]https://us-east-1.console.aws.amazon.com/ses/home 检索 SMTP 端点,例如: email-smtp.us-east-1.amazonaws.com

  3. 如果需要,创建 SMTP 凭据

  4. 如果你处于沙盒环境,请验证收件人(使用电子邮件验证)

Configuration

ses.smtp=...
ses.user=...
ses.password=...
ses.from=an email address from the verified domain

quarkus.mailer.host=${ses.smtp}
quarkus.mailer.port=587
quarkus.mailer.tls=false
quarkus.mailer.username=${ses.user}
quarkus.mailer.password=${ses.password}
quarkus.mailer.start-tls=REQUIRED
quarkus.mailer.login=REQUIRED
quarkus.mailer.from=${ses.from}

quarkus.mailer.mock=false # In dev mode, prevent from using the mock SMTP server

MailJet

Mailjet 集成用在 SMTP 中继上。您将使用此 SMTP 服务器发送电子邮件。

Prerequisites

  1. 创建 MailJet 帐户和 API 密钥/私钥

  2. 发件人地址必须得到验证(SPF + DKIM),并且电子邮件已明确添加到经验证列表中

Configuration

mailjet.smtp-host=in-v3.mailjet.com
mailjet.api-key=...
mailjet.secret-key=...
mailjet.from=the verified sender address

quarkus.mailer.host=${mailjet.smtp-host}
quarkus.mailer.port=465
quarkus.mailer.username=${mailjet.api-key}
quarkus.mailer.password=${mailjet.secret-key}
quarkus.mailer.start-tls=OPTIONAL
quarkus.mailer.tls=true
quarkus.mailer.login=REQUIRED
quarkus.mailer.from=${mailjet.from}

quarkus.mailer.mock=false # In dev mode, prevent from using the mock SMTP server

Sendgrid

Prerequisites

  • 按照说明,使用 DKIM 验证发件人域

Configuration

sendgrid.smtp-host=smtp.sendgrid.net
sendgrid.username=apikey
sendgrid.key=...

quarkus.mailer.host=${sendgrid.smtp-host}
quarkus.mailer.port=465
quarkus.mailer.username=${sendgrid.username}
quarkus.mailer.password=${sendgrid.key}
quarkus.mailer.start-tls=OPTIONAL
quarkus.mailer.login=REQUIRED
quarkus.mailer.from=...
quarkus.mailer.tls=true

quarkus.mailer.mock=false # In dev mode, prevent from using the mock SMTP server

Mailer Configuration Reference

Unresolved include directive in modules/ROOT/pages/mailer-reference.adoc - include::../../../target/quarkus-generated-doc/config/quarkus-mailer.adoc[]