Tworzenie bezserwerowego formularza kontaktowego dla swojej statycznej witryny
Opublikowany: 2022-03-10Generatory stron statycznych stanowią szybką i prostą alternatywę dla systemów zarządzania treścią (CMS), takich jak WordPress. Nie ma konfiguracji serwera ani bazy danych, wystarczy proces kompilacji i prosty kod HTML, CSS i JavaScript. Niestety bez serwera łatwo jest szybko osiągnąć swoje limity. Na przykład w dodaniu formularza kontaktowego.
Wraz z rozwojem architektury bezserwerowej dodanie formularza kontaktowego do statycznej witryny nie musi już być powodem do przejścia na CMS. Można uzyskać to, co najlepsze z obu światów: statyczną witrynę z bezserwerowym zapleczem dla formularza kontaktowego (którego nie trzeba utrzymywać). Być może najlepsze jest to, że w witrynach o małym ruchu, takich jak portfele, wysokie limity wielu dostawców bezserwerowych sprawiają, że te usługi są całkowicie bezpłatne!
W tym artykule poznasz podstawy interfejsów API Amazon Web Services (AWS) Lambda i Simple Email Service (SES), aby zbudować własną statyczną pocztę e-mail w witrynie na platformie Serverless Framework. Pełna usługa przyjmie dane formularza przesłane z żądania AJAX, trafi do punktu końcowego Lambda, przeanalizuje dane w celu zbudowania parametrów SES, wyśle adres e-mail i zwróci odpowiedź dla naszych użytkowników. Przeprowadzę Cię przez pierwszą konfigurację bezserwerową poprzez wdrożenie. Powinno to zająć niecałą godzinę, więc zaczynajmy!

Konfiguracja
Rozpoczęcie korzystania z technologii bezserwerowej wymaga spełnienia minimalnych wymagań wstępnych. Dla naszych celów jest to po prostu środowisko węzła z Yarn, platformą Serverless Framework i kontem AWS.
Konfiguracja projektu

Używamy Yarn do zainstalowania Serverless Framework w lokalnym katalogu.
- Utwórz nowy katalog do obsługi projektu.
- Przejdź do katalogu w interfejsie wiersza poleceń.
- Uruchom
yarn init
, aby utworzyć plikpackage.json
dla tego projektu. - Uruchom
yarn add serverless
, aby zainstalować platformę lokalnie. - Uruchom
yarn serverless create --template aws-nodejs --name static-site-mailer
, aby utworzyć szablon usługi Node i nadaj mu nazwęstatic-site-mailer
.
Nasz projekt jest skonfigurowany, ale nie będziemy w stanie nic zrobić, dopóki nie skonfigurujemy naszych usług AWS.
Konfigurowanie konta Amazon Web Services, poświadczeń i prostej usługi e-mail

Serverless Framework nagrał wideo instruktażowe dotyczące konfigurowania poświadczeń AWS, ale tutaj również wymieniłem kroki.
- Załóż konto AWS lub zaloguj się, jeśli już je posiadasz.
- Na pasku wyszukiwania AWS wyszukaj „IAM”.
- Na stronie uprawnień kliknij „Użytkownicy” na pasku bocznym, a następnie przycisk „Dodaj użytkownika”.
- Na stronie Dodaj użytkownika nadaj użytkownikowi nazwę — odpowiednie jest na przykład „bezserwerowy”. Zaznacz „Dostęp programowy” w obszarze Typ dostępu, a następnie kliknij przycisk Dalej.
- Na ekranie uprawnień kliknij kartę „Dołącz istniejące zasady bezpośrednio”, wyszukaj na liście „Dostęp administratora”, zaznacz ją i kliknij przycisk Dalej.
- Na ekranie przeglądu powinieneś zobaczyć swoją nazwę użytkownika z „Dostępem programistycznym” i „Dostęp administratora”, a następnie utwórz użytkownika.
- Ekran potwierdzenia pokazuje użytkownika „ID klucza dostępu” i „Tajny klucz dostępu”, które będą potrzebne, aby zapewnić dostęp do platformy Serverless Framework. W swoim CLI wpisz
yarn sls config credentials --provider aws --key YOUR_ACCESS_KEY_ID --secret YOUR_SECRET_ACCESS_KEY
, zastępującYOUR_ACCESS_KEY_ID
iYOUR_SECRET_ACCESS_KEY
kluczami na ekranie potwierdzenia.
Twoje dane logowania są teraz skonfigurowane, ale gdy jesteśmy w konsoli AWS, skonfigurujmy prostą usługę poczty e-mail.
- Kliknij Strona główna konsoli w lewym górnym rogu, aby przejść do domu.
- Na stronie głównej, w pasku wyszukiwania AWS, wyszukaj „Prosta usługa e-mail”.
- Na stronie głównej SES kliknij „Adresy e-mail” na pasku bocznym.
- Na stronie z listą adresów e-mail kliknij przycisk „Zweryfikuj nowy adres e-mail”.
- W oknie dialogowym wpisz swój adres e-mail, a następnie kliknij „Zweryfikuj ten adres e-mail”.
- Za chwilę otrzymasz wiadomość e-mail zawierającą link do weryfikacji adresu. Kliknij link, aby zakończyć proces.
Teraz, gdy nasze konta są już gotowe, rzućmy okiem na pliki szablonów Serverless.
Konfigurowanie platformy bezserwerowej
Uruchomienie serverless create
tworzy dwa pliki: handler.js, który zawiera funkcję Lambda, oraz serverless.yml, który jest plikiem konfiguracyjnym dla całej architektury Serverless. W pliku konfiguracyjnym możesz określić tyle funkcji obsługi, ile chcesz, a każdy z nich zostanie odwzorowany na nową funkcję, która może współdziałać z innymi funkcjami. W tym projekcie utworzymy tylko jeden program obsługi, ale w pełnej architekturze bezserwerowej będziesz mieć kilka różnych funkcji usługi.

W handler.js zobaczysz pojedynczą wyeksportowaną funkcję o nazwie hello
. Jest to obecnie główna (i jedyna) funkcja. Wraz ze wszystkimi funkcjami obsługi węzłów przyjmuje trzy parametry:
-
event
Można to traktować jako dane wejściowe funkcji. -
context object
Zawiera informacje o czasie wykonywania funkcji Lambda. -
callback
Opcjonalny parametr do zwracania informacji do wywołującego.
// handler.js 'use strict'; module.exports.hello = (event, context, callback) => { const response = { statusCode: 200, body: JSON.stringify({ message: 'Go Serverless v1.0! Your function executed successfully!', input: event, }), }; callback(null, response); };
Na dole hello
znajduje się wywołanie zwrotne. Jest to opcjonalny argument do zwrócenia odpowiedzi, ale jeśli nie zostanie wywołany jawnie , zwróci niejawnie z null
. Callback przyjmuje dwa parametry:
- Błąd błędu
Za dostarczanie informacji o błędach, gdy sama Lambda ulegnie awarii. Gdy Lambda się powiedzie, do tego parametru należy przekazaćnull
. - Wynik obiektu
Za udostępnienie obiektu odpowiedzi. Musi być zgodny zJSON.stringify
. Jeśli w polu błędu znajduje się parametr, to pole jest ignorowane.
Nasza statyczna strona wyśle dane naszego formularza w treści zdarzenia, a wywołanie zwrotne zwróci odpowiedź, którą zobaczy nasz użytkownik.
W serverless.yml zobaczysz nazwę usługi, informacje o dostawcy i funkcje.
# serverless.yml service: static-site-mailer provider: name: aws runtime: nodejs6.10 functions: hello: handler: handler.hello

Zwróć uwagę na mapowanie między funkcją hello a obsługą? Możemy nazwać nasz plik i funkcję cokolwiek i dopóki mapuje się do konfiguracji, będzie działać. Zmieńmy nazwę naszej funkcji na staticSiteMailer
.
# serverless.yml functions: staticSiteMailer: handler: handler.staticSiteMailer
// handler.js module.exports.staticSiteMailer = (event, context, callback) => { ... };
Funkcje lambda potrzebują uprawnień do interakcji z inną infrastrukturą AWS. Zanim będziemy mogli wysłać wiadomość e-mail, musimy zezwolić na to SES. W serverless.yml, pod provider.iamRoleStatements
dodaj uprawnienia.
# serverless.yml provider: name: aws runtime: nodejs6.10 iamRoleStatements: - Effect: "Allow" Action: - "ses:SendEmail" Resource: ["*"]
Ponieważ potrzebujemy adresu URL dla naszej akcji formularza, musimy dodać zdarzenia HTTP do naszej funkcji. W serverless.yml tworzymy ścieżkę, określamy metodę jako post
i ustawiamy CORS na true dla bezpieczeństwa.
functions: staticSiteMailer: handler: handler.staticSiteMailer events: - http: method: post path: static-site-mailer cors: true
Nasze zaktualizowane pliki serverless.yml i handler.js powinny wyglądać tak:
# serverless.yml service: static-site-mailer provider: name: aws runtime: nodejs6.10 functions: staticSiteMailer: handler: handler.staticSiteMailer events: - http: method: post path: static-site-mailer cors: true provider: name: aws runtime: nodejs6.10 iamRoleStatements: - Effect: "Allow" Action: - "ses:SendEmail" Resource: ["*"]
// handler.js 'use strict'; module.exports.staticSiteMailer = (event, context, callback) => { const response = { statusCode: 200, body: JSON.stringify({ message: 'Go Serverless v1.0! Your function executed successfully!', input: event, }), }; callback(null, response); };
Nasza architektura bezserwerowa jest skonfigurowana, więc wdróżmy ją i przetestujmy. Otrzymasz prostą odpowiedź JSON.
yarn sls deploy --verbose yarn sls invoke --function staticSiteMailer { "statusCode": 200, "body": "{\"message\":\"Go Serverless v1.0! Your function executed successfully!\",\"input\":{}}" }

Tworzenie formularza HTML
Nasze dane wejściowe funkcji Lambda i dane wyjściowe formularza muszą być zgodne, więc zanim zbudujemy funkcję, zbudujemy formularz i przechwycimy jego dane wyjściowe. Dbamy o to, aby było to proste dzięki polam nazwy, adresu e-mail i wiadomości. Dodamy akcję formularza po wdrożeniu naszej architektury bezserwerowej i otrzymaniu adresu URL, ale wiemy, że będzie to żądanie POST, więc możemy to dodać. Na końcu formularza dodajemy tag akapitu do wyświetlania wiadomości odpowiedzi do użytkownika, które zaktualizujemy w odpowiedzi na zgłoszenie zwrotne.

<form action="{{ SERVICE URL }}" method="POST"> <label> Name <input type="text" name="name" required> </label> <label> Email <input type="email" name="reply_to" required> </label> <label> Message: <textarea name="message" required></textarea> </label> <button type="submit">Send Message</button> </form> <p></p>
Aby przechwycić dane wyjściowe, dodajemy procedurę obsługi przesyłania do formularza, przekształcamy nasze parametry formularza w obiekt i wysyłamy skonkretyzowany JSON do naszej funkcji Lambda. W funkcji Lambda do odczytu naszych danych używamy JSON.parse()
. Alternatywnie możesz użyć Serialize lub ciągu zapytania jQuery, aby wysłać i przeanalizować parametry formularza jako ciąg zapytania, ale JSON.stringify()
i JSON.parse()
są natywne.
(() => { const form = document.querySelector('form'); const formResponse = document.querySelector('js-form-response'); form.onsubmit = e => { e.preventDefault(); // Prepare data to send const data = {}; const formElements = Array.from(form); formElements.map(input => (data[input.name] = input.value)); // Log what our lambda function will receive console.log(JSON.stringify(data)); }; })();
Śmiało i prześlij formularz, a następnie przechwyć dane wyjściowe konsoli. Użyjemy go następnie w naszej funkcji Lambda.

Wywoływanie funkcji lambda
Szczególnie podczas programowania musimy przetestować, czy nasza funkcja robi to, czego oczekujemy. Serverless Framework udostępnia invoke local
polecenia invoke
i invoke, które wyzwalają funkcję odpowiednio ze środowisk na żywo i programistycznych . Oba polecenia wymagają przekazania nazwy funkcji, w naszym przypadku staticSiteMailer
.
yarn sls invoke local --function staticSiteMailer
Aby przekazać pozorne dane do naszej funkcji, utwórz nowy plik o nazwie data.json
z przechwyconymi danymi wyjściowymi konsoli pod kluczem body
w obiekcie JSON. Powinien wyglądać mniej więcej tak:
// data.json { "body": "{\"name\": \"Sender Name\",\"reply_to\": \"[email protected]\",\"message\": \"Sender message\"}" }
Aby wywołać funkcję z danymi lokalnymi, przekaż argument --path
wraz ze ścieżką do pliku.
yarn sls invoke local --function staticSiteMailer --path data.json

Zobaczysz odpowiedź podobną do poprzedniej, ale klucz input
będzie zawierał zdarzenie, z którego się wyśmiewaliśmy. Wykorzystajmy nasze pozorne dane, aby wysłać wiadomość e-mail za pomocą prostej usługi e-mail!
Wysyłanie wiadomości e-mail za pomocą prostej usługi e-mail
staticSiteMailer
funkcję staticSiteMailer wywołaniem prywatnej funkcji sendEmail
. Na razie możesz zakomentować lub usunąć kod szablonu i zastąpić go:
// hander.js function sendEmail(formData, callback) { // Build the SES parameters // Send the email } module.exports.staticSiteMailer = (event, context, callback) => { const formData = JSON.parse(event.body); sendEmail(formData, function(err, data) { if (err) { console.log(err, err.stack); } else { console.log(data); } }); };
Najpierw event.body
, aby przechwycić dane z formularza, a następnie przekazujemy je do prywatnej funkcji sendEmail
. sendEmail
jest odpowiedzialny za wysłanie wiadomości e-mail, a funkcja callback zwróci odpowiedź niepowodzenie lub sukces z err
lub data
. W naszym przypadku możemy po prostu zarejestrować błąd lub dane, ponieważ za chwilę zastąpimy je wywołaniem zwrotnym Lambda.
Amazon udostępnia wygodny zestaw SDK, aws-sdk
, do łączenia swoich usług z funkcjami Lambda. Wiele z ich usług, w tym SES, jest tego częścią. Dodajemy go do projektu za pomocą yarn add aws-sdk
i importujemy go na górę pliku handler'a.
// handler.js const AWS = require('aws-sdk'); const SES = new AWS.SES();
W naszej prywatnej funkcji sendEmail
budujemy parametry SES.sendEmail
z przeanalizowanych danych formularza i wykorzystujemy wywołanie zwrotne do zwrócenia odpowiedzi do wywołującego. Parametry wymagają jako obiektu:
- Źródło
Adres e-mail, z którego wysyła SES . - Odpowiedz na adresy
Tablica adresów e-mail dodana do odpowiedzi w polu w wiadomości e-mail. - Miejsce docelowe
Obiekt, który musi zawierać co najmniej jeden ToAddresses , CcAddresses lub BccAddresses . Każde pole pobiera tablicę adresów e-mail odpowiadających odpowiednio polom to , cc i bcc . - Wiadomość
Obiekt zawierający Body i Subject .
Ponieważ formData
jest obiektem, możemy wywołać nasze pola formularza bezpośrednio, jak formData.message
, zbudować nasze parametry i wysłać je. Przekazujemy Twój zweryfikowany przez SES adres e-mail do Source
i Destination.ToAddresses
. Dopóki adres e-mail jest zweryfikowany, możesz podać tutaj wszystko, w tym różne adresy e-mail. Wyciągamy nasze reply_to
, message
i name
z naszego obiektu formData
, aby wypełnić pola ReplyToAddresses
i Message.Body.Text.Data
.
// handler.js function sendEmail(formData, callback) { const emailParams = { Source: '[email protected]', // SES SENDING EMAIL ReplyToAddresses: [formData.reply_to], Destination: { ToAddresses: ['[email protected]'], // SES RECEIVING EMAIL }, Message: { Body: { Text: { Charset: 'UTF-8', Data: `${formData.message}\n\nName: ${formData.name}\nEmail: ${formData.reply_to}`, }, }, Subject: { Charset: 'UTF-8', Data: 'New message from your_site.com', }, }, }; SES.sendEmail(emailParams, callback); }
SES.sendEmail
wyśle e-mail, a nasz callback zwróci odpowiedź. Wywołanie funkcji lokalnej spowoduje wysłanie wiadomości e-mail na zweryfikowany adres.
yarn sls invoke local --function staticSiteMailer --path data.json

SES.sendEmail
, gdy się powiedzie.Zwracanie odpowiedzi od prowadzącego
Nasza funkcja wysyła wiadomość e-mail za pomocą wiersza poleceń, ale nasi użytkownicy nie będą w ten sposób z nią wchodzić w interakcje. Musimy zwrócić odpowiedź na przesłanie formularza AJAX. Jeśli to się nie powiedzie, powinniśmy zwrócić odpowiedni statusCode
oraz err.message
. Gdy się powiedzie, wystarczy 200
statusCode
, ale zwrócimy również odpowiedź mailera w treści. W staticSiteMailer
budujemy nasze dane odpowiedzi i zastępujemy naszą funkcję zwrotną sendEmail
funkcją zwrotną Lambda.
// handler.js module.exports.staticSiteMailer = (event, context, callback) => { const formData = JSON.parse(event.body); sendEmail(formData, function(err, data) { const response = { statusCode: err ? 500 : 200, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': 'https://your-domain.com', }, body: JSON.stringify({ message: err ? err.message : data, }), }; callback(null, response); }); };
Nasze wywołanie zwrotne Lambda zwraca teraz zarówno komunikaty o powodzeniu, jak i niepowodzeniu z SES.sendEmail
. Budujemy odpowiedź, sprawdzając, czy err
, aby nasza odpowiedź była spójna. Sama funkcja zwrotna Lambda przekazuje null
w polu argumentu błędu, a odpowiedź jako drugą. Chcemy przekazać błędy dalej, ale jeśli sama Lambda zawiedzie, jej wywołanie zwrotne zostanie wywołane niejawnie z odpowiedzią na błąd.
W headers
musisz zastąpić Access-Control-Allow-Origin
własną domeną. Uniemożliwi to innym domenom korzystanie z Twojej usługi i potencjalnie naliczanie rachunków AWS w Twoim imieniu! I nie omawiam tego w tym artykule, ale możliwe jest skonfigurowanie Lambdy do korzystania z własnej domeny. Musisz mieć certyfikat SSL/TLS przesłany do Amazon. Zespół Serverless Framework napisał fantastyczny samouczek, jak to zrobić.
Wywołanie funkcji lokalnej spowoduje teraz wysłanie wiadomości e-mail i zwrócenie odpowiedniej odpowiedzi.
yarn sls invoke local --function staticSiteMailer --path data.json

Wywołanie funkcji lambda z formularza
Nasza usługa jest kompletna! Aby go wdrożyć, uruchom yarn sls deploy -v
. Po wdrożeniu otrzymasz adres URL podobny do https://r4nd0mh45h.execute-api.us-east-1.amazonaws.com/dev/static-site-mailer
, który możesz dodać do akcji formularza. Następnie tworzymy żądanie AJAX i zwracamy odpowiedź użytkownikowi.
(() => { const form = document.querySelector('form'); const formResponse = document.querySelector('js-form-response'); form.onsubmit = e => { e.preventDefault(); // Prepare data to send const data = {}; const formElements = Array.from(form); formElements.map(input => (data[input.name] = input.value)); // Log what our lambda function will receive console.log(JSON.stringify(data)); // Construct an HTTP request var xhr = new XMLHttpRequest(); xhr.open(form.method, form.action, true); xhr.setRequestHeader('Accept', 'application/json; charset=utf-8'); xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); // Send the collected data as JSON xhr.send(JSON.stringify(data)); // Callback function xhr.onloadend = response => { if (response.target.status === 200) { // The form submission was successful form.reset(); formResponse.innerHTML = 'Thanks for the message. I'll be in touch shortly.'; } else { // The form submission failed formResponse.innerHTML = 'Something went wrong'; console.error(JSON.parse(response.target.response).message); } }; }; })();
W wywołaniu zwrotnym AJAX sprawdzamy kod statusu za pomocą response.target.status
. Jeśli jest to wartość inna niż 200
, możemy wyświetlić użytkownikowi komunikat o błędzie, w przeciwnym razie poinformuj go, że wiadomość została wysłana. Ponieważ nasza Lambda zwraca łańcuchowy JSON, możemy przeanalizować treść wiadomości za pomocą JSON.parse(response.target.response).message
. Szczególnie przydatne jest rejestrowanie błędu.
Powinieneś być w stanie przesłać formularz w całości ze swojej statycznej witryny!

Następne kroki
Dodanie formularza kontaktowego do statystyk jest łatwe dzięki bezserwerowemu frameworkowi i AWS. W naszym kodzie jest miejsce na ulepszenia, takie jak dodanie walidacji formularzy za pomocą honeypota, zapobieganie wywołaniom AJAX dla nieprawidłowych formularzy i poprawa UX w przypadku odpowiedzi, ale to wystarczy, aby zacząć. Niektóre z tych ulepszeń można zobaczyć w utworzonym przeze mnie repozytorium poczty statycznej witryny. Mam nadzieję, że zainspirowałem Cię do samodzielnego wypróbowania Serverless!