Erstellen eines serverlosen Kontaktformulars für Ihre statische Website
Veröffentlicht: 2022-03-10Statische Website-Generatoren bieten eine schnelle und einfache Alternative zu Content-Management-Systemen (CMS) wie WordPress. Es gibt kein Server- oder Datenbank-Setup, nur einen Build-Prozess und einfaches HTML, CSS und JavaScript. Leider stößt man ohne Server schnell an seine Grenzen. Zum Beispiel beim Hinzufügen eines Kontaktformulars.
Mit dem Aufkommen der serverlosen Architektur muss das Hinzufügen eines Kontaktformulars zu Ihrer statischen Website nicht mehr der Grund sein, zu einem CMS zu wechseln. Es ist möglich, das Beste aus beiden Welten zu bekommen: eine statische Site mit einem serverlosen Back-End für das Kontaktformular (das Sie nicht pflegen müssen). Das Beste ist vielleicht, dass auf Websites mit geringem Datenverkehr, wie z. B. Portfolios, die hohen Limits vieler Serverless-Anbieter diese Dienste völlig kostenlos machen!
In diesem Artikel lernen Sie die Grundlagen von Amazon Web Services (AWS) Lambda und Simple Email Service (SES) APIs kennen, um Ihren eigenen statischen Site-Mailer auf dem Serverless Framework zu erstellen. Der vollständige Service nimmt Formulardaten entgegen, die von einer AJAX-Anfrage übermittelt werden, trifft auf den Lambda-Endpunkt, parst die Daten, um die SES-Parameter zu erstellen, sendet die E-Mail-Adresse und gibt eine Antwort für unsere Benutzer zurück. Ich führe Sie durch die erstmalige Einrichtung von Serverless durch die Bereitstellung. Es sollte weniger als eine Stunde dauern, bis es fertig ist, also fangen wir an!
Einrichten
Es gibt minimale Voraussetzungen für den Einstieg in die serverlose Technologie. Für unsere Zwecke ist es einfach eine Knotenumgebung mit Yarn, dem Serverless Framework und einem AWS-Konto.
Einrichten des Projekts
Wir verwenden Yarn, um das Serverless Framework in einem lokalen Verzeichnis zu installieren.
- Erstellen Sie ein neues Verzeichnis zum Hosten des Projekts.
- Navigieren Sie in Ihrer Befehlszeilenschnittstelle zu dem Verzeichnis.
- Führen Sie wool
yarn init
aus, um einepackage.json
-Datei für dieses Projekt zu erstellen. - Führen Sie
yarn add serverless
aus, um das Framework lokal zu installieren. - Führen
yarn serverless create --template aws-nodejs --name static-site-mailer
, um eine Node-Service-Vorlage zu erstellen, und nennen Sie siestatic-site-mailer
.
Unser Projekt ist eingerichtet, aber wir können nichts tun, bis wir unsere AWS-Services eingerichtet haben.
Einrichten eines Amazon Web Services-Kontos, von Anmeldeinformationen und eines einfachen E-Mail-Dienstes
Das Serverless Framework hat eine Videoanleitung zum Einrichten von AWS-Anmeldeinformationen aufgezeichnet, aber ich habe die Schritte auch hier aufgelistet.
- Registrieren Sie sich für ein AWS-Konto oder melden Sie sich an, wenn Sie bereits eines haben.
- Suchen Sie in der AWS-Suchleiste nach „IAM“.
- Klicken Sie auf der IAM-Seite in der Seitenleiste auf „Benutzer“ und dann auf die Schaltfläche „Benutzer hinzufügen“.
- Geben Sie dem Benutzer auf der Seite „Benutzer hinzufügen“ einen Namen – so etwas wie „serverlos“ ist angemessen. Aktivieren Sie „Programmatischer Zugriff“ unter „Zugriffstyp“ und klicken Sie dann auf „Weiter“.
- Klicken Sie auf dem Berechtigungsbildschirm auf die Registerkarte „Vorhandene Richtlinien direkt anhängen“, suchen Sie in der Liste nach „AdministratorAccess“, aktivieren Sie es und klicken Sie auf „Weiter“.
- Auf dem Überprüfungsbildschirm sollten Sie Ihren Benutzernamen mit „Programmatischer Zugriff“ und „AdministratorAccess“ sehen und dann den Benutzer erstellen.
- Der Bestätigungsbildschirm zeigt die „Zugriffsschlüssel-ID“ und den „Geheimen Zugriffsschlüssel“ des Benutzers an, die Sie benötigen, um dem Serverless Framework Zugriff zu gewähren. Geben Sie in Ihrer CLI
yarn sls config credentials --provider aws --key YOUR_ACCESS_KEY_ID --secret YOUR_SECRET_ACCESS_KEY
und ersetzenYOUR_ACCESS_KEY_ID
undYOUR_SECRET_ACCESS_KEY
durch die Schlüssel auf dem Bestätigungsbildschirm.
Ihre Anmeldeinformationen sind jetzt konfiguriert, aber während wir uns in der AWS-Konsole befinden, richten wir den einfachen E-Mail-Service ein.
- Klicken Sie oben links auf Konsolenstartseite, um nach Hause zu gehen.
- Suchen Sie auf der Startseite in der AWS-Suchleiste nach „Simple Email Service“.
- Klicken Sie auf der SES-Startseite in der Seitenleiste auf „E-Mail-Adressen“.
- Klicken Sie auf der Listenseite der E-Mail-Adressen auf die Schaltfläche „Neue E-Mail-Adresse bestätigen“.
- Geben Sie im Dialogfenster Ihre E-Mail-Adresse ein und klicken Sie dann auf „Diese E-Mail-Adresse bestätigen“.
- Sie erhalten in Kürze eine E-Mail mit einem Link zur Bestätigung der Adresse. Klicken Sie auf den Link, um den Vorgang abzuschließen.
Nachdem unsere Konten erstellt wurden, werfen wir einen Blick auf die Serverless-Vorlagendateien.
Einrichten des serverlosen Frameworks
Beim Ausführen von serverless create
werden zwei Dateien erstellt: handler.js, die die Lambda-Funktion enthält, und serverless.yml, die Konfigurationsdatei für die gesamte serverlose Architektur. Innerhalb der Konfigurationsdatei können Sie beliebig viele Handler angeben, und jeder wird einer neuen Funktion zugeordnet, die mit anderen Funktionen interagieren kann. In diesem Projekt erstellen wir nur einen einzigen Handler, aber in einer vollständigen serverlosen Architektur hätten Sie mehrere der verschiedenen Funktionen des Dienstes.
In handler.js sehen Sie eine einzelne exportierte Funktion namens hello
. Dies ist derzeit die wichtigste (und einzige) Funktion. Zusammen mit allen Node-Handlern benötigt es drei Parameter:
-
event
Dies kann man sich als Eingabedaten für die Funktion vorstellen. -
context object
Diese enthält die Laufzeitinformationen der Lambda-Funktion. -
callback
Ein optionaler Parameter, um Informationen an den Aufrufer zurückzugeben.
// 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); };
Am Ende von hello
steht ein Rückruf. Es ist ein optionales Argument, um eine Antwort zurückzugeben, aber wenn es nicht explizit aufgerufen wird, wird es implizit mit null
zurückgegeben. Der Callback benötigt zwei Parameter:
- Fehler Fehler
Zum Bereitstellen von Fehlerinformationen, wenn Lambda selbst fehlschlägt. Wenn Lambda erfolgreich ist, solltenull
an diesen Parameter übergeben werden. - Objektergebnis
Zum Bereitstellen eines Response-Objekts. Es mussJSON.stringify
kompatibel sein. Wenn das Fehlerfeld einen Parameter enthält, wird dieses Feld ignoriert.
Unsere statische Website sendet unsere Formulardaten im Ereignistext und der Rückruf gibt eine Antwort zurück, die unser Benutzer sehen kann.
In serverless.yml sehen Sie den Namen des Dienstes, Anbieterinformationen und die Funktionen.
# serverless.yml service: static-site-mailer provider: name: aws runtime: nodejs6.10 functions: hello: handler: handler.hello
Beachten Sie die Zuordnung zwischen der Hello-Funktion und dem Handler? Wir können unsere Datei beliebig benennen und funktionieren, und solange sie der Konfiguration entspricht, wird sie funktionieren. Benennen wir unsere Funktion in staticSiteMailer
um.
# serverless.yml functions: staticSiteMailer: handler: handler.staticSiteMailer
// handler.js module.exports.staticSiteMailer = (event, context, callback) => { ... };
Lambda-Funktionen benötigen die Berechtigung, mit anderer AWS-Infrastruktur zu interagieren. Bevor wir eine E-Mail senden können, müssen wir SES dies erlauben. Fügen Sie in serverless.yml unter provider.iamRoleStatements
die Berechtigung hinzu.
# serverless.yml provider: name: aws runtime: nodejs6.10 iamRoleStatements: - Effect: "Allow" Action: - "ses:SendEmail" Resource: ["*"]
Da wir eine URL für unsere Formularaktion benötigen, müssen wir unserer Funktion HTTP-Ereignisse hinzufügen. In serverless.yml erstellen wir einen Pfad, geben die Methode als post
an und setzen CORS aus Sicherheitsgründen auf true.
functions: staticSiteMailer: handler: handler.staticSiteMailer events: - http: method: post path: static-site-mailer cors: true
Unsere aktualisierten serverless.yml- und handler.js-Dateien sollten wie folgt aussehen:
# 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); };
Unsere serverlose Architektur ist eingerichtet, also stellen wir sie bereit und testen sie. Sie erhalten eine einfache JSON-Antwort.
yarn sls deploy --verbose yarn sls invoke --function staticSiteMailer { "statusCode": 200, "body": "{\"message\":\"Go Serverless v1.0! Your function executed successfully!\",\"input\":{}}" }
Erstellen des HTML-Formulars
Die Eingabe unserer Lambda-Funktion und die Ausgabe des Formulars müssen übereinstimmen. Bevor wir also die Funktion erstellen, erstellen wir das Formular und erfassen dessen Ausgabe. Wir halten es mit Namens-, E-Mail- und Nachrichtenfeldern einfach. Wir fügen die Formularaktion hinzu, sobald wir unsere serverlose Architektur bereitgestellt und unsere URL erhalten haben, aber wir wissen, dass es sich um eine POST-Anforderung handeln wird, damit wir sie hinzufügen können. Am Ende des Formulars fügen wir ein Absatz-Tag zur Anzeige hinzu Antwortnachrichten an den Benutzer, die wir beim Übermittlungsrückruf aktualisieren.
<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>
Um die Ausgabe zu erfassen, fügen wir dem Formular einen Submit-Handler hinzu, wandeln unsere Formularparameter in ein Objekt um und senden stringifiziertes JSON an unsere Lambda-Funktion. In der Lambda-Funktion verwenden wir JSON.parse()
, um unsere Daten zu lesen. Alternativ können Sie Serialize oder query-string von jQuery verwenden, um die Formularparameter als Abfragezeichenfolge zu senden und zu analysieren, aber JSON.stringify()
und JSON.parse()
sind nativ.
(() => { 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)); }; })();
Senden Sie Ihr Formular ab und erfassen Sie dann die Konsolenausgabe. Wir werden es als nächstes in unserer Lambda-Funktion verwenden.
Aufrufen von Lambda-Funktionen
Besonders während der Entwicklung müssen wir testen, ob unsere Funktion das tut, was wir erwarten. Das Serverless Framework stellt den Befehl invoke
und invoke invoke local
bereit, um Ihre Funktion von Live- bzw. Entwicklungsumgebungen auszulösen. Beide Befehle erfordern die Übergabe des Funktionsnamens, in unserem Fall staticSiteMailer
.
yarn sls invoke local --function staticSiteMailer
Um Scheindaten an unsere Funktion zu übergeben, erstellen Sie eine neue Datei namens data.json
mit der erfassten Konsolenausgabe unter einem body
innerhalb eines JSON-Objekts. Es sollte in etwa so aussehen:
// data.json { "body": "{\"name\": \"Sender Name\",\"reply_to\": \"[email protected]\",\"message\": \"Sender message\"}" }
Um die Funktion mit den lokalen Daten aufzurufen, übergeben Sie das Argument --path
zusammen mit dem Pfad zur Datei.
yarn sls invoke local --function staticSiteMailer --path data.json
Sie sehen eine ähnliche Antwort wie zuvor, aber die input
enthält das Ereignis, das wir verspottet haben. Lassen Sie uns unsere Scheindaten verwenden, um eine E-Mail mit dem einfachen E-Mail-Dienst zu senden!
Senden einer E-Mail mit dem einfachen E-Mail-Dienst
Wir werden die staticSiteMailer
Funktion durch einen Aufruf einer privaten sendEmail
Funktion ersetzen. Im Moment können Sie den Vorlagencode auskommentieren oder entfernen und ihn ersetzen durch:
// 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); } }); };
Zuerst analysieren wir event.body
, um die Formulardaten zu erfassen, und übergeben sie dann an eine private sendEmail
Funktion. sendEmail
ist für das Senden der E-Mail verantwortlich, und die Callback-Funktion gibt eine Fehler- oder Erfolgsantwort mit err
oder data
zurück. In unserem Fall können wir den Fehler oder die Daten einfach protokollieren, da wir diese gleich durch den Lambda-Callback ersetzen werden.
Amazon bietet ein praktisches SDK, aws-sdk
, um seine Dienste mit Lambda-Funktionen zu verbinden. Viele ihrer Dienste, einschließlich SES, sind Teil davon. Wir fügen es dem Projekt mit yarn add aws-sdk
und importieren es in die oberste Handler-Datei.
// handler.js const AWS = require('aws-sdk'); const SES = new AWS.SES();
In unserer privaten sendEmail
Funktion bauen wir die SES.sendEmail
Parameter aus den geparsten Formulardaten auf und verwenden den Callback, um eine Antwort an den Aufrufer zurückzugeben. Die Parameter benötigen als Objekt:
- Quelle
Die E-Mail-Adresse, von der SES sendet. - ReplyToAddresses
Ein Array von E-Mail-Adressen, die der Antwort auf das Feld in der E-Mail hinzugefügt wurden. - Ziel
Ein Objekt, das mindestens eine ToAddresses , CcAddresses oder BccAddresses enthalten muss. Jedes Feld enthält eine Reihe von E-Mail-Adressen, die den Feldern to , cc und bcc entsprechen. - Nachricht
Ein Objekt, das Body und Subject enthält.
Da formData
ein Objekt ist, können wir unsere Formularfelder direkt wie formData.message
, unsere Parameter erstellen und senden. Wir leiten Ihre SES-verifizierte E-Mail an Source
and Destination.ToAddresses
weiter. Solange die E-Mail verifiziert ist, können Sie hier alles übergeben, einschließlich verschiedener E-Mail-Adressen. Wir pflücken unser reply_to
, message
und name
aus unserem formData
-Objekt, um die Felder ReplyToAddresses
und 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
sendet die E-Mail und unser Rückruf gibt eine Antwort zurück. Beim Aufrufen der lokalen Funktion wird eine E-Mail an Ihre verifizierte Adresse gesendet.
yarn sls invoke local --function staticSiteMailer --path data.json
Zurückgeben einer Antwort vom Handler
Unsere Funktion sendet eine E-Mail über die Befehlszeile, aber so werden unsere Benutzer nicht damit interagieren. Wir müssen eine Antwort auf unsere AJAX-Formularübermittlung zurücksenden. Wenn dies fehlschlägt, sollten wir einen entsprechenden statusCode
sowie die err.message
. Wenn es erfolgreich ist, ist der 200
statusCode
ausreichend, aber wir geben die Mailer-Antwort auch im Body zurück. In staticSiteMailer
bauen wir unsere Antwortdaten auf und ersetzen unsere sendEmail
Callback-Funktion durch den Lambda-Callback.
// 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); }); };
Unser Lambda-Callback gibt jetzt sowohl Erfolgs- als auch Fehlermeldungen von SES.sendEmail
. Wir erstellen die Antwort mit Überprüfungen, ob err
vorhanden ist, damit unsere Antwort konsistent ist. Die Lambda-Callback-Funktion selbst übergibt null
im Fehlerargumentfeld und die Antwort als zweites. Wir wollen Fehler weitergeben, aber wenn Lambda selbst fehlschlägt, wird sein Rückruf implizit mit der Fehlerantwort aufgerufen.
In den headers
müssen Sie Access-Control-Allow-Origin
durch Ihre eigene Domain ersetzen. Dadurch wird verhindert, dass andere Domains Ihren Service nutzen und möglicherweise eine AWS-Rechnung in Ihrem Namen anhäufen! Und ich gehe in diesem Artikel nicht darauf ein, aber es ist möglich, Lambda so einzurichten, dass es Ihre eigene Domain verwendet. Sie müssen ein SSL/TLS-Zertifikat auf Amazon hochladen. Das Serverless Framework-Team hat dazu ein fantastisches Tutorial geschrieben.
Beim Aufrufen der lokalen Funktion wird nun eine E-Mail gesendet und die entsprechende Antwort zurückgegeben.
yarn sls invoke local --function staticSiteMailer --path data.json
Aufrufen der Lambda-Funktion aus dem Formular
Unser Service ist komplett! Um es bereitzustellen, führen Sie das yarn sls deploy -v
. Nach der Bereitstellung erhalten Sie eine URL, die in etwa so aussieht wie https://r4nd0mh45h.execute-api.us-east-1.amazonaws.com/dev/static-site-mailer
, die Sie der Formularaktion hinzufügen können. Als nächstes erstellen wir die AJAX-Anforderung und senden die Antwort an den Benutzer zurück.
(() => { 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); } }; }; })();
Im AJAX-Callback prüfen wir den Statuscode mit response.target.status
. Wenn es etwas anderes als 200
ist, können wir dem Benutzer eine Fehlermeldung anzeigen, andernfalls lassen Sie ihn wissen, dass die Nachricht gesendet wurde. Da unser Lambda stringifiziertes JSON zurückgibt, können wir die Textnachricht mit JSON.parse(response.target.response).message
. Es ist besonders nützlich, den Fehler zu protokollieren.
Sie sollten in der Lage sein, Ihr Formular vollständig von Ihrer statischen Website aus einzureichen!
Nächste Schritte
Das Hinzufügen eines Kontaktformulars zu Ihrer Statik ist mit dem Serverless Framework und AWS ganz einfach. Es gibt Raum für Verbesserungen in unserem Code, wie das Hinzufügen von Formularvalidierungen mit einem Honeypot, das Verhindern von AJAX-Aufrufen für ungültige Formulare und das Verbessern der UX der Antwort, aber das reicht für den Anfang. Sie können einige dieser Verbesserungen in dem statischen Site-Mailer-Repo sehen, das ich erstellt habe. Ich hoffe, ich habe Sie dazu inspiriert, Serverless selbst auszuprobieren!