AWS 无服务器入门 - 电子邮件

2025-06-10

AWS 无服务器入门 - 电子邮件

TL;DR

在本系列中,我将尝试讲解 AWS 上无服务器的基础知识,以便您构建自己的无服务器应用程序。在上一篇文章中,我们学习了如何使用 Step-Functions 编排 AWS 服务。在本文中,我们将深入探讨 SES,这项服务允许您从应用程序发送电子邮件。

⬇️ 我会定期发布无服务器内容,如果你想了解更多 ⬇️

在 Twitter 上关注我🚀

快速公告:我还在开发一个名为🛡 sls-mentor 🛡的库。它汇集了 30 条无服务器最佳实践,这些实践会在您的 AWS 无服务器项目中自动检查(无论使用哪种框架)。它是免费开源的,欢迎随时查看!

在 Github 上查找 sls-mentor⭐️

介绍

在本系列中,我将尝试讲解 AWS 上无服务器的基础知识,以便您构建自己的无服务器应用程序。我们已经学习了如何创建 Lambda 函数、如何与数据库交互、如何进行文件存储、如何进行身份验证等等。现在是时候在您的应用程序中实现一些副作用了。电子邮件是一个很好的开端,它允许您向用户发送收据,或者例如发送密码重置链接。

本文将讨论两个主题:

  • 如何设置 SES 以从您自己的域名发送电子邮件,以获得专业的外观
  • 如何通过 Lambda 函数向用户发送简单的模板电子邮件

以下是我们将构建的应用程序的最终架构:

最终架构

这是一个实用的介绍,如果您想深入了解 SES 理论,我建议您阅读我几个月前写的一篇旧文章,其中介绍了更高级的主题,如声誉管理、DKIM、附件、UI 等。

从您自己的域名发送电子邮件

在本部分中,您将设置如何使用您自己的域名发送电子邮件。这将使邮件看起来更专业,并有助于您保持良好的声誉。缺点:它要求您拥有一个域名,而域名并非免费。如果您不想花钱,我将在下一部分介绍一个免费的替代方案。

购买域名

如果您还没有域名,可以在 AWS 或任何其他注册商处购买。我的域名 (pchol.fr) 是在 AWS 上购买的,本教程中会用到它。唯一需要做的就是能够管理域名的 DNS 记录。

在 Amazon Route53 中设置托管区域

要使用您自己的域名发送电子邮件,您需要向 AWS 证明您拥有该域名。为此,您需要在 Amazon Route53 中创建一个托管区域。它是一项 DNS 服务,允许您管理您的域名。您需要将其连接到您在上一步中购买的域名。

与之前的所有文章一样,您将使用 AWS CDK。如果您需要复习如何设置 CDK 项目,请查看本系列的第一篇文章。要创建托管区域,可以使用以下代码:

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import path from 'path';

const DOMAIN_NAME = 'pchol.fr'; // Replace with your own domain name

export class LearnServerlessStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const hostedZone = new cdk.aws_route53.HostedZone(this, 'hostedZone', {
      zoneName: DOMAIN_NAME,
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

让我们运行第一个部署,它应该在 Route53 中预置一个托管区域。这里有一个手动步骤需要执行:您需要使用您的域名验证托管区域。为此,请前往 Route53 控制台,复制已创建的 4 条 DNS 记录。然后,前往您的域名注册商,将这 4 条记录添加到您的域名。它应该如下所示:

获取 NS 记录

添加 NS 记录

完成此步骤后,您已成功验证您的托管区域,并且它将允许您稍后从您的域名发送电子邮件!

设置 SES 来发送电子邮件

现在是设置 SES 的时间了,我们将提供两种资源:

  • SES 身份将授权您从 AWS 发送电子邮件。
  • 电子邮件模板,允许您从应用程序发送模板电子邮件。

创建 SES 身份

// previous code inside the constructor...

const identity = new cdk.aws_ses.EmailIdentity(this, 'sesIdentity', {
  identity: cdk.aws_ses.Identity.publicHostedZone(hostedZone),
});

/*
If you want to send emails for free, use a real email address to create the SES identity.

const MY_EMAIL_ADDRESS = 'john@gmail.com'; // Replace it with a REAL email address you own
const identity = new cdk.aws_ses.EmailIdentity(this, 'sesIdentity', {
  identity: cdk.aws_ses.Identity.email(MY_EMAIL_ADDRESS)
});
*/
Enter fullscreen mode Exit fullscreen mode

代码非常简单,只需创建一个 SES 身份,并将上一步创建的托管区域传递给它即可。它将自动在您的托管区域中创建 DNS(CNAME)记录,向 AWS 证明您拥有该域名,并允许您从该域名发送电子邮件。

如果您跳过了第一部分并且没有域名,请使用注释掉的代码创建身份。它将允许您免费发送电子邮件,但邮件将来自真实的电子邮件地址,而不是您的域名。您需要在下次部署时点击 AWS 发送的链接来验证电子邮件地址。

创建 SES 模板

SES 模板允许您发送包含 HTML 和 CSS 的电子邮件。第一步,在您的项目中创建一个模板文件,其中包含电子邮件的 HTML 和 CSS。我创建了一个简单的模板,如下所示:

export const emailHtmlTemplate = `<html>
  <head>
    <style>
      * {
        font-family: sans-serif;
        text-align: center;
        padding: 0;
        margin: 0;
      }
      .title {
        color: #fff;
        background: #17bb90;
        padding: 1em;
      }
      .container {
        border: 2px solid #17bb90;
        border-radius: 1em;
        margin: 1em auto;
        max-width: 500px;
        overflow: hidden;
      }
      .message {
        padding: 1em;
        line-height: 1.5em;
        color: #033c49;
      }
      .footer {
        font-size: .8em;
        color: #888;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="title">
        <h1>Hello {{username}}!</h1>
      </div>
      <div class="message">
        <p>{{message}}</p>
      </div>
    </div>
    <p class="footer">This is an automated message, please do not try to answer</p>
  </body>
</html>`;
Enter fullscreen mode Exit fullscreen mode

它包含两个占位符,{{username}}{{message}},它们将被您传递给模板的值替换。占位符必须遵循此{{...}}语法,您可以根据需要使用任意数量的占位符。

使用 CSS 时要小心,某些属性(例如 flex ⚠️)并非所有电子邮件客户端都支持。您可以访问此网站,了解哪些属性受哪些电子邮件客户端支持。另一个解决方案是使用 CSS 框架,它可以为您完成复杂的兼容性工作。我使用的是MJML,在我关于 SES 的深入文章中,我解释了如何使用它来创建响应式电子邮件。

最后一步,使用 CDK 提供 SES 模板:

import { emailHtmlTemplate } from './emailHtmlTemplate';

// previous code inside the constructor...

const emailTemplate = new cdk.aws_ses.CfnTemplate(this, 'emailTemplate', {
  template: {
    htmlPart: emailHtmlTemplate,
    subjectPart: 'Hello {{username}}!',
  },
});
Enter fullscreen mode Exit fullscreen mode

非常简单,唯一新的部分是subjectPart。它包含电子邮件的主题,也可以包含占位符。

从 Lambda 函数发送电子邮件

您已准备好发送第一封电子邮件!为此,我们将创建一个用于发送电子邮件的 lambda 函数。它将由 API 网关端点触发,并接收收件人的用户名、消息和电子邮件地址作为正文。然后,它将使用我们之前创建的模板向收件人发送电子邮件。

首先,让我们创建 lambda 函数的配置代码:

// previous code inside the constructor...

const sendEmail = new cdk.aws_lambda_nodejs.NodejsFunction(this, 'sendEmail', {
  entry: path.join(__dirname, 'sendEmail', 'handler.ts'),
  handler: 'handler',
  environment: {
    SENDER_EMAIL: `contact@${identity.emailIdentityName}`, // you can use what you want before the @
    // SENDER_EMAIL: MY_EMAIL_ADDRESS, // Use this line if you don't own a domain name
    TEMPLATE_NAME: emailTemplate.ref,
  },
});

sendEmail.addToRolePolicy(
  new cdk.aws_iam.PolicyStatement({
    actions: ['ses:SendTemplatedEmail'],
    resources: [`*`],
  }),
);

const myFirstApi = new cdk.aws_apigateway.RestApi(this, 'myFirstApi', {});
const sendEmailResource = myFirstApi.root.addResource('send-email');
sendEmailResource.addMethod('POST', new cdk.aws_apigateway.LambdaIntegration(sendEmail));
Enter fullscreen mode Exit fullscreen mode

这段代码做了三件事:

  • 它创建了一个带有两个环境变量的 lambda 函数:SENDER_EMAILTEMPLATE_NAME。第一个包含发件人的电子邮件地址,第二个包含我们之前创建的模板的名称。
  • 它授予 lambda 函数使用操作发送电子邮件的权限ses:SendTemplatedEmail。目标资源是*,这意味着 lambda 函数可以向任何电子邮件地址发送电子邮件。
  • 它创建一个 POST API 网关端点,当调用时将触发 lambda 函数。

最后让我们创建 lambda 函数的代码:

import { SESv2Client, SendEmailCommand } from '@aws-sdk/client-sesv2';

const sesClient = new SESv2Client({});

export const handler = async ({ body }: { body: string }) => {
  const senderEmail = process.env.SENDER_EMAIL;
  const templateName = process.env.TEMPLATE_NAME;

  if (!senderEmail || !templateName) {
    throw new Error('Missing environment variables');
  }

  const { username, email, message } = JSON.parse(body) as { username?: string; email?: string; message?: string };

  if (!username || !email || !message) {
    return {
      statusCode: 400,
      body: JSON.stringify({ message: 'Missing parameters' }),
    };
  }

  const formattedMessage = message.replace(/\n/g, '<br />');

  await sesClient.send(
    new SendEmailCommand({
      FromEmailAddress: senderEmail,
      Content: {
        Template: {
          TemplateName: templateName,
          TemplateData: JSON.stringify({ username, message: formattedMessage }),
        },
      },
      Destination: {
        ToAddresses: [email],
      },
    }),
  );

  return {
    statusCode: 200,
    body: JSON.stringify({ message: 'Email sent' }),
  };
};
Enter fullscreen mode Exit fullscreen mode

这段代码做了三件事:

  • 它解析环境变量(在最后的代码片段中设置)和请求的主体。
  • 它将消息格式化为替换\n<br />以便消息在电子邮件的 HTML 中正确显示。
  • 我使用 AWS SDK 发送了一封电子邮件。它使用了SendEmailCommand。此命令采用 3 个参数:
    • FromEmailAddress:发件人的电子邮件地址。该地址必须在 SES 中进行验证。(这也是我们之前创建身份的原因)
    • Content:电子邮件的内容。它包含模板名称和用于替换占位符的数据。⚠️数据必须包含与模板中占位符完全相同的键。
    • Destination:收件人的电子邮件地址。

部署堆栈后您就可以发送第一封电子邮件了!

npm run cdk deploy
Enter fullscreen mode Exit fullscreen mode

测试时间

最后一步(抱歉……),如果您刚刚创建了第一个 SES 身份,那么您的 AWS 账户肯定在 SES 沙盒中。这是一种防止人们发送垃圾邮件的保护机制。这意味着您只能向已验证的电子邮件地址发送电子邮件。要测试您的代码,您需要验证您的电子邮件地址。您可以在 SES 控制台中手动创建一个与要发送电子邮件的电子邮件地址对应的新身份来完成此操作。

验证电子邮件地址

现在,您可以通过向 API 端点发送带有正确主体的 POST 请求来测试您的代码。我使用\n分隔符在消息中添加了新行。

发送电子邮件

以下是它在我的邮箱中的样子:

已收到电子邮件

结论

本教程是对 SES 的浅显介绍。这只是第一步,如果您计划发送专业的邮件,同时保持域名的良好声誉,则需要进一步了解 SES。我推荐您阅读我之前写的一篇关于 SES 的文章,该文章对这项服务进行了深入的探讨。

我计划每两个月更新一次这一系列文章。我已经介绍了如何创建简单的 Lambda 函数和 REST API,以及如何与 DynamoDB 数据库和 S3 存储桶进行交互。您可以在我的代码库中关注这些进展!我将介绍一些新的主题,例如创建事件驱动的应用程序、类型安全等等。如果您有任何建议,请随时联系我!

如果您能回复并分享这篇文章给您的朋友和同事,我将不胜感激。这将极大地帮助我扩大读者群。另外,别忘了订阅,以便及时收到下一篇文章的更新!

如果您想与我保持联系,请访问我的Twitter 账号。我经常发布或转发有关 AWS 和无服务器的有趣内容,欢迎关注我!

鏂囩珷鏉ユ簮锛�https://dev.to/slsbytheodo/learn-serverless-on-aws-step-by-step-emails-49hp
PREV
开始使用 AWS 无服务器 - SQS
NEXT
AWS 无服务器入门 - 身份验证