使用 SendGrid 在 Spring Boot 中发送电子邮件

在本文中,我们探讨如何使用 SendGrid 从 Spring Boot 应用程序发送电子邮件。

我们完成了必要的配置,并实现了发送简单电子邮件、带有附件的电子邮件和带有动态模板的 HTML 电子邮件的功能。

最后,为了验证我们的应用程序向 SendGrid 发送了正确的请求,我们使用 MockServer 编写了一个集成测试。


发送电子邮件是现代网络应用程序的重要功能,无论是用于用户注册、密码重置还是促销活动。

在本教程中,我们将探讨如何在Spring Boot应用程序中使用SendGrid发送电子邮件。我们将介绍必要的配置并针对不同用例实现电子邮件发送功能。

设置 SendGrid
要遵循本教程,我们首先需要一个 SendGrid 帐户。SendGrid 提供免费套餐,允许我们每天发送最多 100 封电子邮件,这对于我们的演示来说已经足够了。

一旦我们注册了,我们就需要创建一个API 密钥 来验证我们对 SendGrid 服务的请求。

最后,我们需要验证发件人身份才能成功发送电子邮件。

设置项目
在我们开始使用 SendGrid 发送电子邮件之前,我们需要包含 SDK 依赖项并正确配置我们的应用程序。

依赖项
让我们首先将SendGrid SDK 依赖项添加到项目的pom.xml文件中:

<dependency>
    <groupId>com.sendgrid</groupId>
    <artifactId>sendgrid-java</artifactId>
    <version>4.10.2</version>
</dependency>

这种依赖关系为我们提供了与 SendGrid 服务交互并从我们的应用程序发送电子邮件所需的类。

定义 SendGrid 配置属性
现在,为了与 SendGrid 服务交互并向我们的用户发送电子邮件,我们需要配置 API 密钥来验证 API 请求。我们还需要配置发件人的姓名和电子邮件地址,这些应该与我们在 SendGrid 帐户中设置的发件人身份相匹配。

我们将这些属性存储在项目的application.yaml文件中,并使用@ConfigurationProperties将值映射到 POJO,我们的服务层在与 SendGrid 交互时引用该 POJO:

@Validated
@ConfigurationProperties(prefix = "com.jdon.sendgrid")
class SendGridConfigurationProperties {
    @NotBlank
    @Pattern(regexp = "^SG[0-9a-zA-Z._]{67}$")
    private String apiKey;
    @Email
    @NotBlank
    private String fromEmail;
    @NotBlank
    private String fromName;
    // standard setters and getters
}

我们还添加了验证注释,以确保所有必需的属性都已正确配置。如果任何定义的验证失败,Spring ApplicationContext将无法启动。这使我们能够遵循快速失败原则。

下面是我们的application.yaml文件的片段,它定义了将自动映射到我们的SendGridConfigurationProperties类的必需属性:

com:
  jdon:
    sendgrid:
      api-key: ${SENDGRID_API_KEY}
      from-email: ${SENDGRID_FROM_EMAIL}
      from-name: ${SENDGRID_FROM_NAME}

我们使用${}属性占位符从环境变量中加载属性值。因此,此设置允许我们将 SendGrid 属性外部化,并在我们的应用程序中轻松访问它们。

配置 SendGrid Bean
现在我们已经配置了我们的属性,让我们引用它们来定义必要的 bean:

@Configuration
@EnableConfigurationProperties(SendGridConfigurationProperties.class)
class SendGridConfiguration {
    private final SendGridConfigurationProperties sendGridConfigurationProperties;
    // standard constructor
    @Bean
    public SendGrid sendGrid() {
        String apiKey = sendGridConfigurationProperties.getApiKey();
        return new SendGrid(apiKey);
    }
}

使用构造函数注入,我们注入了之前创建的SendGridConfigurationProperties类的一个实例。然后我们使用配置的 API 密钥创建一个SendGrid bean。

接下来,我们将创建一个 bean 来代表所有发出的电子邮件的发件人:

@Bean
public Email fromEmail() {
    String fromEmail = sendGridConfigurationProperties.getFromEmail();
    String fromName = sendGridConfigurationProperties.getFromName();
    return new Email(fromEmail, fromName);
}

有了这些 bean,我们可以在我们的服务层中自动连接它们以便与 SendGrid 服务进行交互。

发送简单的电子邮件
现在我们已经定义了 bean,让我们创建一个EmailDispatcher类并引用它们来发送一封简单的电子邮件:

private static final String EMAIL_ENDPOINT = "mail/send";
public void dispatchEmail(String emailId, String subject, String body) {
    Email toEmail = new Email(emailId);
    Content content = new Content("text/plain", body);
    Mail mail = new Mail(fromEmail, subject, toEmail, content);
    Request request = new Request();
    request.setMethod(Method.POST);
    request.setEndpoint(EMAIL_ENDPOINT);
    request.setBody(mail.build());
    sendGrid.api(request);
}

在我们的dispatchEmail()方法中,我们创建一个新的Mail对象来代表我们要发送的电子邮件,然后将其设置为我们的Request对象的请求正文。

最后,我们使用SendGrid bean 将请求发送到 SendGrid 服务。

发送带附件的电子邮件
除了发送简单的纯文本电子邮件之外,SendGrid还允许我们发送带有附件的电子邮件。

首先,我们将创建一个辅助方法,将MultipartFile转换为来自 SendGrid SDK 的Attachments对象:

private Attachments createAttachment(MultipartFile file) {
    byte[] encodedFileContent = Base64.getEncoder().encode(file.getBytes());
    Attachments attachment = new Attachments();
    attachment.setDisposition("attachment");
    attachment.setType(file.getContentType());
    attachment.setFilename(file.getOriginalFilename());
    attachment.setContent(new String(encodedFileContent, StandardCharsets.UTF_8));
    return attachment;
}

在我们的createAttachment()方法中,我们创建一个新的Attachments对象并根据MultipartFile参数设置其属性。

值得注意的是,在将文件内容设置到附件对象之前,我们要对其进行 Base64 编码。

接下来,让我们更新dispatchEmail()方法以接受MultipartFile对象的可选列表:

public void dispatchEmail(String emailId, String subject, String body, List<MultipartFile> files) {
    // ... same as above
    if (files != null && !files.isEmpty()) {
        for (MultipartFile file : files) {
            Attachments attachment = createAttachment(file);
            mail.addAttachments(attachment);
        }
    }
    // ... same as above
}

我们遍历文件参数中的每个文件,使用createAttachment()方法创建其对应的附件对象,并将其添加到Mail对象中。该方法的其余部分保持不变。

使用动态模板发送电子邮件
SendGrid 还允许我们使用 HTML 和Handlebars 语法创建动态电子邮件模板。

为了演示,我们将举一个例子,向我们的用户发送个性化的水合警报电子邮件。

创建 HTML 模板
首先,我们将为补水警报电子邮件创建一个 HTML 模板:

<html>
    <head>
        <style>
            body { font-family: Arial; line-height: 2; text-align: Center; }
            h2 { color: DeepSkyBlue; }
            .alert { background: Red; color: White; padding: 1rem; font-size: 1.5rem; font-weight: bold; }
            .message { border: .3rem solid DeepSkyBlue; padding: 1rem; margin-top: 1rem; }
            .status { background: LightCyan; padding: 1rem; margin-top: 1rem; }
        </style>
    </head>
    <body>
        <div class="alert">⚠ URGENT HYDRATION ALERT ⚠</div>
        <div class="message">
            <h2>It's time to drink water!</h2>
            <p>Hey {{name}}, this is your friendly reminder to stay hydrated. Your body will thank you!</p>
            <div class="status">
                <p><strong>Last drink:</strong> {{lastDrinkTime}}</p>
                <p><strong>Hydration status:</strong> {{hydrationStatus}}</p>
            </div>
        </div>
    </body>
</html>