Creazione di un modulo di contatto serverless per il tuo sito statico
Pubblicato: 2022-03-10I generatori di siti statici forniscono un'alternativa rapida e semplice ai sistemi di gestione dei contenuti (CMS) come WordPress. Non c'è nessuna configurazione di server o database, solo un processo di compilazione e semplici HTML, CSS e JavaScript. Sfortunatamente, senza un server, è facile raggiungere rapidamente i propri limiti. Ad esempio, aggiungendo un modulo di contatto.
Con l'avvento dell'architettura serverless, l'aggiunta di un modulo di contatto al tuo sito statico non deve più essere il motivo per passare a un CMS. È possibile ottenere il meglio da entrambi i mondi: un sito statico con un back-end serverless per il modulo di contatto (che non è necessario mantenere). Forse la cosa migliore è che nei siti a basso traffico, come i portafogli, i limiti elevati di molti provider serverless rendono questi servizi completamente gratuiti!
In questo articolo imparerai le nozioni di base su Amazon Web Services (AWS) Lambda e API Simple Email Service (SES) per creare il tuo mailer di sito statico su Serverless Framework. Il servizio completo prenderà i dati dei moduli inviati da una richiesta AJAX, raggiungerà l'endpoint Lambda, analizzerà i dati per creare i parametri SES, invierà l'indirizzo e-mail e restituirà una risposta per i nostri utenti. Ti guiderò attraverso la configurazione di Serverless per la prima volta attraverso la distribuzione. Il completamento dovrebbe richiedere meno di un'ora, quindi iniziamo!

Impostare
Ci sono prerequisiti minimi per iniziare con la tecnologia Serverless. Per i nostri scopi, è semplicemente un ambiente nodo con Yarn, il Framework Serverless e un account AWS.
Allestimento del progetto

Usiamo Yarn per installare Serverless Framework in una directory locale.
- Crea una nuova directory per ospitare il progetto.
- Passare alla directory nell'interfaccia della riga di comando.
- Esegui
yarn init
per creare un filepackage.json
per questo progetto. - Esegui
yarn add serverless
per installare il framework in locale. - Esegui
yarn serverless create --template aws-nodejs --name static-site-mailer
per creare un modello di servizio Node e denominarlostatic-site-mailer
.
Il nostro progetto è pronto, ma non saremo in grado di fare nulla finché non avremo configurato i nostri servizi AWS.
Configurazione di un account Amazon Web Services, credenziali e un servizio di posta elettronica semplice

Il Serverless Framework ha registrato una procedura dettagliata video per la configurazione delle credenziali AWS, ma ho elencato anche i passaggi qui.
- Registrati per un account AWS o accedi se ne hai già uno.
- Nella barra di ricerca di AWS, cerca "IAM".
- Nella pagina IAM, fai clic su "Utenti" nella barra laterale, quindi sul pulsante "Aggiungi utente".
- Nella pagina Aggiungi utente, dai un nome all'utente: qualcosa come "serverless" è appropriato. Seleziona "Accesso programmatico" in Tipo di accesso, quindi fai clic su Avanti.
- Nella schermata delle autorizzazioni, fai clic sulla scheda "Allega direttamente le politiche esistenti", cerca "Accesso amministratore" nell'elenco, selezionalo e fai clic su Avanti.
- Nella schermata di revisione dovresti vedere il tuo nome utente, con "Accesso programmatico" e "Accesso amministratore", quindi creare l'utente.
- La schermata di conferma mostra l'"ID chiave di accesso" e la "Chiave di accesso segreta" dell'utente, che ti serviranno per fornire l'accesso a Serverless Framework. Nella tua CLI, digita
yarn sls config credentials --provider aws --key YOUR_ACCESS_KEY_ID --secret YOUR_SECRET_ACCESS_KEY
, sostituendoYOUR_ACCESS_KEY_ID
eYOUR_SECRET_ACCESS_KEY
con le chiavi nella schermata di conferma.
Le tue credenziali sono ora configurate, ma mentre siamo nella console AWS configuriamo Simple Email Service.
- Fai clic su Console Home nell'angolo in alto a sinistra per tornare a casa.
- Nella home page, nella barra di ricerca di AWS, cerca "Simple Email Service".
- Nella home page di SES, fai clic su "Indirizzi e-mail" nella barra laterale.
- Nella pagina dell'elenco Indirizzi e-mail, fai clic sul pulsante "Verifica un nuovo indirizzo e-mail".
- Nella finestra di dialogo, digita il tuo indirizzo e-mail, quindi fai clic su "Verifica questo indirizzo e-mail".
- Riceverai in pochi istanti un'e-mail contenente un link per verificare l'indirizzo. Fare clic sul collegamento per completare la procedura.
Ora che i nostri account sono stati creati, diamo un'occhiata ai file del modello Serverless.
Configurazione del framework serverless
L'esecuzione di serverless create
crea due file: handler.js che contiene la funzione Lambda e serverless.yml che è il file di configurazione per l'intera architettura serverless. All'interno del file di configurazione, puoi specificare tutti i gestori che desideri e ognuno verrà mappato su una nuova funzione che può interagire con altre funzioni. In questo progetto creeremo un solo gestore, ma in un'architettura serverless completa avresti molte delle varie funzioni del servizio.

In handler.js vedrai una singola funzione esportata denominata hello
. Questa è attualmente la funzione principale (e unica). Insieme a tutti i gestori di nodi, accetta tre parametri:
-
event
Questo può essere considerato come i dati di input per la funzione. -
context object
Contiene le informazioni di runtime della funzione Lambda. -
callback
Un parametro facoltativo per restituire informazioni al chiamante.
// 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); };
In fondo a hello
, c'è una richiamata. È un argomento facoltativo per restituire una risposta, ma se non viene chiamato in modo esplicito , verrà restituito implicitamente con null
. La richiamata accetta due parametri:
- Errore errore
Per fornire informazioni sull'errore per quando la Lambda stessa ha esito negativo. Quando Lambda ha esito positivo,null
dovrebbe essere passato a questo parametro. - Risultato oggetto
Per fornire un oggetto di risposta. Deve essere compatibile conJSON.stringify
. Se è presente un parametro nel campo di errore, questo campo viene ignorato.
Il nostro sito statico invierà i dati del nostro modulo nel corpo dell'evento e la richiamata restituirà una risposta che il nostro utente può vedere.
In serverless.yml vedrai il nome del servizio, le informazioni sul provider e le funzioni.
# serverless.yml service: static-site-mailer provider: name: aws runtime: nodejs6.10 functions: hello: handler: handler.hello

Notare la mappatura tra la funzione ciao e il gestore? Possiamo nominare il nostro file e funzionare in qualsiasi modo e fintanto che si associa alla configurazione funzionerà. Rinominiamo la nostra funzione in staticSiteMailer
.
# serverless.yml functions: staticSiteMailer: handler: handler.staticSiteMailer
// handler.js module.exports.staticSiteMailer = (event, context, callback) => { ... };
Le funzioni Lambda richiedono l'autorizzazione per interagire con altre infrastrutture AWS. Prima di poter inviare un'e-mail, dobbiamo consentire a SES di farlo. In serverless.yml, in provider.iamRoleStatements
aggiungi l'autorizzazione.
# serverless.yml provider: name: aws runtime: nodejs6.10 iamRoleStatements: - Effect: "Allow" Action: - "ses:SendEmail" Resource: ["*"]
Poiché abbiamo bisogno di un URL per la nostra azione modulo, dobbiamo aggiungere eventi HTTP alla nostra funzione. In serverless.yml creiamo un percorso, specifichiamo il metodo come post
e impostiamo CORS su true per sicurezza.
functions: staticSiteMailer: handler: handler.staticSiteMailer events: - http: method: post path: static-site-mailer cors: true
I nostri file serverless.yml e handler.js aggiornati dovrebbero assomigliare a:
# 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); };
La nostra architettura serverless è configurata, quindi distribuiamola e testiamola. Riceverai una semplice risposta JSON.
yarn sls deploy --verbose yarn sls invoke --function staticSiteMailer { "statusCode": 200, "body": "{\"message\":\"Go Serverless v1.0! Your function executed successfully!\",\"input\":{}}" }

Creazione del modulo HTML
L'input della nostra funzione Lambda e l'output del modulo devono corrispondere, quindi prima di creare la funzione creeremo il modulo e ne cattureremo l'output. Lo manteniamo semplice con i campi nome, e-mail e messaggio. Aggiungeremo l'azione del modulo dopo aver distribuito la nostra architettura serverless e ottenuto il nostro URL, ma sappiamo che sarà una richiesta POST, quindi possiamo aggiungerla. Alla fine del modulo, aggiungiamo un tag di paragrafo per la visualizzazione messaggi di risposta all'utente che aggiorneremo al callback di invio.

<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>
Per acquisire l'output, aggiungiamo un gestore di invio al modulo, trasformiamo i nostri parametri del modulo in un oggetto e inviamo JSON stringato alla nostra funzione Lambda. Nella funzione Lambda utilizziamo JSON.parse()
per leggere i nostri dati. In alternativa, puoi utilizzare Serialize o query-string di jQuery per inviare e analizzare i parametri del modulo come stringa di query, ma JSON.stringify()
e JSON.parse()
sono nativi.
(() => { 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)); }; })();
Vai avanti e invia il tuo modulo, quindi acquisisci l'output della console. Lo useremo successivamente nella nostra funzione Lambda.

Invocare le funzioni Lambda
Soprattutto durante lo sviluppo, dobbiamo testare che la nostra funzione faccia ciò che ci aspettiamo. Serverless Framework fornisce il comando invoke
e invoke local
per attivare la funzione rispettivamente dagli ambienti live e di sviluppo . Entrambi i comandi richiedono il nome della funzione passato, nel nostro caso staticSiteMailer
.
yarn sls invoke local --function staticSiteMailer
Per passare dati fittizi nella nostra funzione, crea un nuovo file denominato data.json
con l'output della console acquisito sotto una chiave body
all'interno di un oggetto JSON. Dovrebbe assomigliare a qualcosa come:
// data.json { "body": "{\"name\": \"Sender Name\",\"reply_to\": \"[email protected]\",\"message\": \"Sender message\"}" }
Per richiamare la funzione con i dati locali, passare l'argomento --path
insieme al percorso del file.
yarn sls invoke local --function staticSiteMailer --path data.json

Vedrai una risposta simile a prima, ma la chiave di input
conterrà l'evento che abbiamo deriso. Usiamo i nostri dati fittizi per inviare un'e-mail utilizzando Simple Email Service!
Invio di un'e-mail con un servizio di posta elettronica semplice
Sostituiremo la funzione staticSiteMailer
con una chiamata a una funzione sendEmail
privata. Per ora puoi commentare o rimuovere il codice del modello e sostituirlo con:
// 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); } }); };
Innanzitutto, event.body
per acquisire i dati del modulo, quindi lo passiamo a una funzione sendEmail
privata. sendEmail
è responsabile dell'invio dell'e-mail e la funzione di richiamata restituirà una risposta non riuscita o riuscita con err
o data
. Nel nostro caso, possiamo semplicemente registrare l'errore o i dati poiché lo sostituiremo con il callback Lambda tra un momento.
Amazon fornisce un comodo SDK, aws-sdk
, per connettere i propri servizi con le funzioni Lambda. Molti dei loro servizi, incluso SES, ne fanno parte. Lo aggiungiamo al progetto con yarn add aws-sdk
e lo importiamo nella parte superiore del file handler.
// handler.js const AWS = require('aws-sdk'); const SES = new AWS.SES();
Nella nostra funzione privata sendEmail
, costruiamo i parametri SES.sendEmail
dai dati del modulo analizzati e utilizziamo la richiamata per restituire una risposta al chiamante. I parametri richiedono quanto segue come oggetto:
- Fonte
L'indirizzo email da cui SES sta inviando. - ReplyToAddresses
Una matrice di indirizzi e-mail aggiunti alla risposta al campo nell'e-mail. - Destinazione
Un oggetto che deve contenere almeno un ToAddresses , CcAddresses o BccAddresses . Ciascun campo accetta una matrice di indirizzi e-mail che corrispondono rispettivamente ai campi to , cc e bcc . - Messaggio
Un oggetto che contiene il Corpo e il Soggetto .
Poiché formData
è un oggetto, possiamo chiamare i nostri campi modulo direttamente come formData.message
, costruire i nostri parametri e inviarlo. Passiamo la tua email verificata SES a Source
e Destination.ToAddresses
. Finché l'e-mail è verificata, puoi passare qualsiasi cosa qui, inclusi diversi indirizzi e-mail. Estraiamo il nostro reply_to
, message
e name
il nostro oggetto formData
per compilare i campi ReplyToAddresses
e 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
invierà l'e-mail e la nostra richiamata restituirà una risposta. Richiamando la funzione locale verrà inviata un'e-mail al tuo indirizzo verificato.
yarn sls invoke local --function staticSiteMailer --path data.json

SES.sendEmail
quando riesce.Restituzione di una risposta dal gestore
La nostra funzione invia un'e-mail utilizzando la riga di comando, ma non è così che i nostri utenti interagiranno con essa. Dobbiamo restituire una risposta al nostro invio del modulo AJAX. Se fallisce, dovremmo restituire uno statusCode
appropriato oltre a err.message
. Quando riesce, 200
statusCode
è sufficiente, ma restituiremo anche la risposta del mailer nel corpo. In staticSiteMailer
creiamo i nostri dati di risposta e sostituiamo la nostra funzione di richiamata sendEmail
con la richiamata 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); }); };
La nostra richiamata Lambda ora restituisce sia i messaggi di successo che quelli di errore da SES.sendEmail
. Costruiamo la risposta con controlli se è presente un err
in modo che la nostra risposta sia coerente. La stessa funzione di callback Lambda passa null
nel campo dell'argomento dell'errore e la risposta come seconda. Vogliamo passare gli errori in avanti, ma se Lambda stesso fallisce, il suo callback verrà chiamato implicitamente con la risposta all'errore.
Nelle headers
, dovrai sostituire Access-Control-Allow-Origin
con il tuo dominio. Ciò impedirà a qualsiasi altro dominio di utilizzare il tuo servizio e potenzialmente di accumulare una fattura AWS a tuo nome! E non lo tratterò in questo articolo, ma è possibile configurare Lambda per utilizzare il proprio dominio. Dovrai avere un certificato SSL/TLS caricato su Amazon. Il team di Serverless Framework ha scritto un fantastico tutorial su come farlo.
Invocare la funzione locale ora invierà un'e-mail e restituirà la risposta appropriata.
yarn sls invoke local --function staticSiteMailer --path data.json

Chiamare la funzione Lambda dal modulo
Il nostro servizio è completo! Per distribuirlo, esegui yarn sls deploy -v
. Una volta distribuito, otterrai un URL simile a https://r4nd0mh45h.execute-api.us-east-1.amazonaws.com/dev/static-site-mailer
che puoi aggiungere all'azione del modulo. Successivamente, creiamo la richiesta AJAX e restituiamo la risposta all'utente.
(() => { 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); } }; }; })();
Nel callback AJAX, controlliamo il codice di stato con response.target.status
. Se è qualcosa di diverso da 200
possiamo mostrare un messaggio di errore all'utente, altrimenti fagli sapere che il messaggio è stato inviato. Poiché il nostro Lambda restituisce JSON stringato, possiamo analizzare il messaggio del corpo con JSON.parse(response.target.response).message
. È particolarmente utile registrare l'errore.
Dovresti essere in grado di inviare il tuo modulo interamente dal tuo sito statico!

Prossimi passi
Aggiungere un modulo di contatto al tuo statico è facile con Serverless Framework e AWS. C'è spazio per miglioramenti nel nostro codice, come aggiungere la convalida dei moduli con un honeypot, prevenire le chiamate AJAX per moduli non validi e migliorare l'UX se la risposta, ma questo è sufficiente per iniziare. Puoi vedere alcuni di questi miglioramenti all'interno del repository di posta statica del sito che ho creato. Spero di averti ispirato a provare Serverless tu stesso!