Сочетание S3 и CloudFront позволяет легко и недорого разместить статический сайт. Но вот вопрос: что происходит, когда вы запрашиваете HTML-форму. Рассмотрим реализацию простого бессерверного бэкенда для этой цели!
Введение
AWS S3 и Cloudfront предлагают экономичный и масштабируемый способ для размещения статического сайта, например целевой страницы (англ. landing page) для стартапа или страницы подписки на новостные рассылки. Однако хотелось бы узнать, как реализовать форму без сервера.
В данной статье мы создадим бессерверный бэкенд для формы, задействуя AWS Lambda и API Gateway. Так мы активируем отправку электронного письма с заполненной информацией из формы на предустановленный email-адрес с помощью AWS SES.
Предварительные требования
Перед работой с кодом Lambda необходимо настроить среду разработки и аккаунт AWS с учетом следующих моментов.
- Инструменты локальной разработки AWS Serverless Application Model (сокр. SAM, Модель бессерверного приложения AWS) устанавливаются в соответствии с инструкциями.
- Сервис электронной почты AWS SES настраивается не в “песочнице”, а в режиме продакшн. Домен проверяется в соответствии с инструкциями.
- Имеется сайт с HTML-формой, настраиваемой для отправки данных в конечную точку шлюза API, создание которого будет рассмотрено в дальнейшем материале статьи.
Реализация
Для развертывания Lambda и сопутствующих ресурсов используется SAM.
Начинаем со структуры каталога. Она включает: 1) директорию исходных файлов приложения; 2) директорию скриптов, предназначенных для развертывания приложения; 3) файлы верхнего уровня, например template.yaml
, применяемые SAM.
my-serverless-form/
├── cmd/
│ └── deploy.sh
├── src/
| ├── package.json
│ └── index.js
├── .gitignore
└── template.yaml
template.yaml
выглядит так:
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
serverless-form
SAM project for serverless-form
Parameters:
LambdaVersion:
Default: "not-specified"
Description: "What is the current code version?"
Type: String
Resources:
FormSubmissionFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/
Handler: index.handler
Runtime: nodejs12.x
MemorySize: 128
Timeout: 3
Environment:
Variables:
LAMBDA_VERSION: !Ref LambdaVersion
Policies:
- AWSLambdaBasicExecutionRole
- Version: "2012-10-17" # Policy Document
Statement:
- Effect: Allow
Action:
- ses:SendEmail
- ses:SendRawEmail
Resource: "*"
Events:
APIRoot:
Type: Api
Properties:
Path: /
Method: POST
RestApiId: !Ref FormSubmissionApi
FormSubmissionApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Cors:
AllowMethods: "'POST'"
AllowOrigin: "'https://my-domain.com'"
AllowHeaders: "'x-requested-with'"
Outputs:
FormSubmissionApi:
Description: "API Gateway endpoint URL"
Value: !Sub "https://${FormSubmissionApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
FormSubmissionFunction:
Description: "Lambda Function ARN"
Value: !GetAtt FormSubmissionFunction.Arn
Данный шаблон определяет 2 вида ресурсов.
FormSubmissionFunction
— Lambda, которая при выполнении отправляет электронное письмо.FormSubmissionApi
— шлюз API, получающий публичные запросы из интернета и запускающий Lambda.
Код лямбда-функции указывается в файле index.js
, как показано в примере:
const aws = require("aws-sdk"); // Библиотека для взаимодействия с ресурсами AWS
const ses = new aws.SES({ region: "us-east-1" });
exports.handler = async (eventObject, context, callback) => {
// Запись лога с данными события для удобства отладки
console.log(
"Received event:",
JSON.stringify(eventObject, context, callback)
);
const emailParams = {
Destination: {
ToAddresses: ["[email protected]"],
},
Message: {
Body: {
Text: {
Data:
JSON.stringify(eventObject, context, callback) // TODO: Format your data for email here
},
},
Subject: { Data: "Contact Us Website Form" },
},
Source: "[email protected]",
};
// Отправка электронного письма
await ses.sendEmail(emailParams).promise();
// Ответ API
const response = {
statusCode: 204,
headers: {
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Origin": "https://my-domain.com",
"Access-Control-Allow-Methods": "OPTIONS,POST",
},
isBase64Encoded: false,
body: null,
};
return response;
};
Мы отправляем электронное письмо с помощью SES, содержащего данные из eventObject
. Этот объект включает в себя тело и заголовок запроса, запускающего выполнение Lambda. Вы можете форматировать тело электронного письма по своему усмотрению.
Для парсинга данных формы в Lambda рекомендую следующую библиотеку. Это модуль nodejs
, который выполняет парсинг многокомпонентной формы, содержащей файлы и поля из объекта события AWS Lambda. Он отлично справляется с парсингом двоичных и текстовых файлов.
Для развертывания Lambda запускаем файл script.sh
и следуем инструкциям на экране:
#!/bin/bash
# Выход в случае неудачного выполнения любой команды
set -e
script_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
sam deploy \
--template-file ${script_path}/../template.yaml \
--stack-name serverless-website-form-production \
--capabilities CAPABILITY_IAM \
--guided
Заключение
Мы рассмотрели способ создания бессерверного бэкенда, используемого для приема входящих данных формы (или других данных из запросов API) и отправки электронного письма, содержащего эти данные.
Перед развертыванием подобных бессерверных приложений в продакшн целесообразно реализовать инструмент reCaptcha (или его аналоги). Он не позволяет ботам обнаружить конечную точку и отправить ложные данные. Кому хочется платить за время выполнения Lambda, потраченное на отправку электронных писем от ботов?!
Читайте также:
- Как извлечь показатели из устаревшей системы в реальном времени, не изменив ни строчки кода
- Введение в конвейерную обработку данных с использованием бессерверной архитектуры
- Разветвление на различные очереди SQS с помощью фильтрации сообщений SNS
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Paulo Carvalho: Create a Serverless Form for a Serverless Website