Crearea unui formular de contact fără server pentru site-ul dvs. static

Publicat: 2022-03-10
Rezumat rapid ↬ Cu ajutorul acestui articol, veți putea în sfârșit să învățați elementele de bază ale API-urilor Amazon Web Services (AWS) Lambda și Simple Email Service (SES) pentru a vă ajuta să vă construiți propriul site de e-mail static pe Serverless Framework. Să începem!

Generatoarele statice de site oferă o alternativă rapidă și simplă la sistemele de management al conținutului (CMS) precum WordPress. Nu există setări de server sau baze de date, doar un proces de construire și HTML, CSS și JavaScript simplu. Din păcate, fără un server, este ușor să-și atingi rapid limitele. De exemplu, în adăugarea unui formular de contact.

Odată cu creșterea arhitecturii fără server, adăugarea unui formular de contact pe site-ul dvs. static nu mai trebuie să fie motivul pentru a trece la un CMS. Este posibil să obțineți tot ce este mai bun din ambele lumi: un site static cu un back-end fără server pentru formularul de contact (pe care nu trebuie să îl întrețineți). Poate cel mai bine, în site-urile cu trafic redus, cum ar fi portofoliile, limitele ridicate ale multor furnizori fără server fac aceste servicii complet gratuite!

În acest articol, veți învăța elementele de bază ale API-urilor Amazon Web Services (AWS) Lambda și Simple Email Service (SES) pentru a vă crea propriul site de e-mail static în cadrul Serverless Framework. Serviciul complet va prelua datele trimise dintr-o solicitare AJAX, va atinge punctul final Lambda, va analiza datele pentru a construi parametrii SES, va trimite adresa de e-mail și va returna un răspuns pentru utilizatorii noștri. Vă voi ghida prin configurarea Serverless pentru prima dată prin implementare. Ar trebui să dureze mai puțin de o oră, așa că să începem!

Formularul static al site-ului, care trimite mesajul la punctul final Lambda și returnează un răspuns utilizatorului.
Formularul static al site-ului, care trimite mesajul la punctul final Lambda și returnează un răspuns utilizatorului.
Mai multe după săritură! Continuați să citiți mai jos ↓

Configurare

Există cerințe minime pentru a începe cu tehnologia Serverless. Pentru scopurile noastre, este pur și simplu un Node Environment cu Yarn, Serverless Framework și un cont AWS.

Stabilirea Proiectului

Site-ul web Serverless Framework. Util pentru instalare și documentare.
Site-ul web Serverless Framework. Util pentru instalare și documentare.

Folosim Yarn pentru a instala Serverless Framework într-un director local.

  1. Creați un director nou pentru a găzdui proiectul.
  2. Navigați la directorul din interfața de linie de comandă.
  3. Rulați yarn init pentru a crea un fișier package.json pentru acest proiect.
  4. Rulați yarn add serverless pentru a instala cadrul local.
  5. Rulați yarn serverless create --template aws-nodejs --name static-site-mailer pentru a crea un șablon de serviciu Node și denumește-l static-site-mailer .

Proiectul nostru este configurat, dar nu vom putea face nimic până când nu vom configura serviciile noastre AWS.

Configurarea unui cont Amazon Web Services, acreditărilor și a unui serviciu simplu de e-mail

Pagina de înscriere Amazon Web Services, care include un nivel generos gratuit, permițând proiectului nostru să fie complet gratuit.
Pagina de înscriere Amazon Web Services, care include un nivel generos gratuit, permițând proiectului nostru să fie complet gratuit.

Serverless Framework a înregistrat o prezentare video pentru configurarea acreditărilor AWS, dar am enumerat și pașii aici.

  1. Înscrieți-vă pentru un cont AWS sau conectați-vă dacă aveți deja unul.
  2. În bara de căutare AWS, căutați „IAM”.
  3. Pe pagina IAM, faceți clic pe „Utilizatori” din bara laterală, apoi pe butonul „Adăugați utilizator”.
  4. Pe pagina Adăugați utilizator, dați utilizatorului un nume – ceva de genul „fără server” este potrivit. Bifați „Acces programatic” sub Tip de acces, apoi faceți clic pe Următorul.
  5. Pe ecranul de permisiuni, faceți clic pe fila „Atașați direct politicile existente”, căutați „AdministratorAccess” în listă, verificați-l și faceți clic pe Următorul.
  6. Pe ecranul de revizuire ar trebui să vedeți numele dvs. de utilizator, cu „Acces programat” și „Acces Administrator”, apoi creați utilizatorul.
  7. Ecranul de confirmare arată utilizatorului „ID-ul cheii de acces” și „Cheia de acces secretă”, veți avea nevoie de acestea pentru a oferi cadru fără server acces. În CLI, tastați yarn sls config credentials --provider aws --key YOUR_ACCESS_KEY_ID --secret YOUR_SECRET_ACCESS_KEY , înlocuind YOUR_ACCESS_KEY_ID și YOUR_SECRET_ACCESS_KEY cu tastele de pe ecranul de confirmare.

Acreditările dvs. sunt configurate acum, dar în timp ce ne aflăm în consola AWS, să setăm Serviciul de e-mail simplu.

  1. Faceți clic pe Console Home în colțul din stânga sus pentru a merge acasă.
  2. Pe pagina de pornire, în bara de căutare AWS, căutați „Serviciul simplu de e-mail”.
  3. Pe pagina de pornire SES, faceți clic pe „Adrese de e-mail” din bara laterală.
  4. Pe pagina de listă cu adrese de e-mail, faceți clic pe butonul „Verificați o nouă adresă de e-mail”.
  5. În fereastra de dialog, introduceți adresa de e-mail, apoi faceți clic pe „Verificați această adresă de e-mail”.
  6. Veți primi în câteva momente un e-mail care conține un link pentru a verifica adresa. Faceți clic pe link pentru a finaliza procesul.

Acum că ne-am făcut conturile, să aruncăm o privire la fișierele șablon Serverless.

Configurarea cadrului fără server

Rularea serverless create creează două fișiere: handler.js care conține funcția Lambda și serverless.yml, care este fișierul de configurare pentru întreaga arhitectură Serverless. În fișierul de configurare, puteți specifica cât de mulți handleri doriți, iar fiecare va fi mapat la o nouă funcție care poate interacționa cu alte funcții. În acest proiect, vom crea doar un singur handler, dar într-o arhitectură completă fără server, veți avea mai multe dintre diferitele funcții ale serviciului.

Structura implicită de fișiere generată din cadrul Serverless Framework care conține handler.js și serverless.yml.
Structura implicită de fișiere generată din cadrul Serverless Framework care conține handler.js și serverless.yml.

În handler.js, veți vedea o singură funcție exportată numită hello . Aceasta este în prezent funcția principală (și singura). Acesta, împreună cu toți gestionanții Node, iau trei parametri:

  • event
    Aceasta poate fi considerată ca fiind datele de intrare pentru funcție.
  • context object
    Acesta conține informațiile de rulare ale funcției Lambda.
  • callback
    Un parametru opțional pentru a returna informații apelantului.
 // 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); };

În partea de jos a hello , există un apel invers. Este un argument opțional pentru a returna un răspuns, dar dacă nu este apelat în mod explicit , va reveni implicit cu null . Reapelarea are doi parametri:

  • Eroare eroare
    Pentru furnizarea de informații despre eroare atunci când Lambda în sine eșuează. Când Lambda reușește, null ar trebui să fie trecut în acest parametru.
  • Rezultatul obiectului
    Pentru furnizarea unui obiect de răspuns. Trebuie să fie compatibil JSON.stringify . Dacă există un parametru în câmpul de eroare, acest câmp este ignorat.

Site-ul nostru static va trimite datele formularului nostru în corpul evenimentului, iar apelul înapoi va returna un răspuns pe care să îl vadă utilizatorul nostru.

În serverless.yml veți vedea numele serviciului, informațiile despre furnizor și funcțiile.

 # serverless.yml service: static-site-mailer provider: name: aws runtime: nodejs6.10 functions: hello: handler: handler.hello 
Cum se atribuie funcția din serverless.yml la handler.js.
Cum se atribuie funcția din serverless.yml la handler.js.

Observați maparea dintre funcția hello și handler? Ne putem numi fișierul și funcționa orice și atâta timp cât se mapează la configurație, va funcționa. Să redenumim funcția în staticSiteMailer .

 # serverless.yml functions: staticSiteMailer: handler: handler.staticSiteMailer
 // handler.js module.exports.staticSiteMailer = (event, context, callback) => { ... };

Funcțiile Lambda au nevoie de permisiunea de a interacționa cu altă infrastructură AWS. Înainte de a putea trimite un e-mail, trebuie să permitem SES să facă acest lucru. În serverless.yml, sub provider.iamRoleStatements adăugați permisiunea.

 # serverless.yml provider: name: aws runtime: nodejs6.10 iamRoleStatements: - Effect: "Allow" Action: - "ses:SendEmail" Resource: ["*"]

Deoarece avem nevoie de o adresă URL pentru acțiunea din formular, trebuie să adăugăm evenimente HTTP la funcția noastră. În serverless.yml creăm o cale, specificăm metoda ca post și setăm CORS la true pentru securitate.

 functions: staticSiteMailer: handler: handler.staticSiteMailer events: - http: method: post path: static-site-mailer cors: true

Fișierele noastre actualizate serverless.yml și handler.js ar trebui să arate astfel:

 # 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); };

Arhitectura noastră fără server este configurată, așa că haideți să o implementăm și să o testăm. Veți primi un răspuns simplu JSON.

 yarn sls deploy --verbose yarn sls invoke --function staticSiteMailer { "statusCode": 200, "body": "{\"message\":\"Go Serverless v1.0! Your function executed successfully!\",\"input\":{}}" } 
Răspunsul de întoarcere de la invocarea noii noastre funcții fără server.
Răspunsul de întoarcere de la invocarea noii noastre funcții fără server.

Crearea formularului HTML

Intrarea funcției noastre Lambda și ieșirea formularului trebuie să se potrivească, așa că înainte de a construi funcția vom construi formularul și vom captura rezultatul acestuia. Îl menținem simplu cu câmpuri de nume, e-mail și mesaje. Vom adăuga acțiunea de formular după ce am implementat arhitectura noastră fără server și am primit adresa URL, dar știm că va fi o solicitare POST, astfel încât să o putem adăuga. La sfârșitul formularului, adăugăm o etichetă de paragraf pentru afișare. mesaje de răspuns către utilizator, pe care le vom actualiza la apelarea trimisă.

 <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>

Pentru a captura rezultatul, adăugăm un handler de trimitere la formular, transformăm parametrii formularului într-un obiect și trimitem JSON stringificat la funcția noastră Lambda. În funcția Lambda folosim JSON.parse() pentru a citi datele noastre. Alternativ, puteți folosi jQuery Serialize sau query-string pentru a trimite și analiza parametrii formularului ca șir de interogare, dar JSON.stringify() și JSON.parse() sunt native.

 (() => { 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)); }; })();

Continuați și trimiteți formularul, apoi capturați rezultatul consolei. Îl vom folosi în funcția noastră Lambda în continuare.

Captarea datelor din formular într-un jurnal de consolă.
Captarea datelor din formular într-un jurnal de consolă.

Invocarea funcțiilor Lambda

Mai ales în timpul dezvoltării, trebuie să testăm funcția noastră face ceea ce ne așteptăm. Cadrul Serverless oferă comanda invoke local invoke a declanșa funcția dvs. din mediile live și, respectiv, de dezvoltare . Ambele comenzi necesită trecerea numelui funcției, în cazul nostru staticSiteMailer .

 yarn sls invoke local --function staticSiteMailer

Pentru a transfera date simulate în funcția noastră, creați un fișier nou numit data.json cu ieșirea din consolă capturată sub o cheie de body dintr-un obiect JSON. Ar trebui să arate ceva de genul:

 // data.json { "body": "{\"name\": \"Sender Name\",\"reply_to\": \"[email protected]\",\"message\": \"Sender message\"}" }

Pentru a invoca funcția cu datele locale, transmiteți argumentul --path împreună cu calea către fișier.

 yarn sls invoke local --function staticSiteMailer --path data.json 
Un răspuns de returnare actualizat de la funcția noastră fără server atunci când îi transmitem date JSON.
Un răspuns de returnare actualizat de la funcția noastră fără server atunci când îi transmitem date JSON.

Veți vedea un răspuns similar cu cel dinainte, dar tasta de input va conține evenimentul pe care l-am batjocorit. Să folosim datele noastre simulate pentru a trimite un e-mail folosind Serviciul de e-mail simplu!

Trimiterea unui e-mail cu serviciul de e-mail simplu

Vom înlocui funcția staticSiteMailer cu un apel la o funcție privată sendEmail . Deocamdată puteți comenta sau elimina codul șablonului și îl puteți înlocui cu:

 // 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); } }); };

Mai întâi, analizăm event.body pentru a captura datele formularului, apoi îl transmitem unei funcții private sendEmail . sendEmail este responsabil pentru trimiterea e-mailului, iar funcția de apel invers va returna un răspuns de eșec sau de succes cu err sau data . În cazul nostru, putem pur și simplu să înregistrăm eroarea sau datele, deoarece le vom înlocui cu apelul Lambda într-un moment.

Amazon oferă un SDK convenabil, aws-sdk , pentru conectarea serviciilor lor cu funcțiile Lambda. Multe dintre serviciile lor, inclusiv SES, fac parte din aceasta. Îl adăugăm la proiect cu yarn add aws-sdk și îl importăm în partea de sus a fișierului handler.

 // handler.js const AWS = require('aws-sdk'); const SES = new AWS.SES();

În funcția noastră privată sendEmail , construim parametrii SES.sendEmail din datele din formularul analizat și folosim apelul invers pentru a returna un răspuns apelantului. Parametrii necesită următoarele ca obiect:

  • Sursă
    Adresa de e-mail SES o trimite de la .
  • Răspunde la adrese
    O serie de adrese de e-mail adăugate la răspunsul la câmpul din e-mail.
  • Destinaţie
    Un obiect care trebuie să conţină cel puţin o ToAddresses , CcAddresses sau BccAddresses . Fiecare câmp are o serie de adrese de e-mail care corespund câmpurilor la , cc și, respectiv, bcc .
  • Mesaj
    Un obiect care conține Corpul și Subiectul .

Deoarece formData este un obiect, putem apela câmpurile noastre de formular direct ca formData.message , construim parametrii și trimitem. Vă transmitem e -mailul verificat SES către Source și Destination.ToAddresses . Atâta timp cât e-mailul este verificat, puteți transmite orice aici, inclusiv adrese de e-mail diferite. reply_to , message și name obiectului nostru formData pentru a completa câmpurile 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 va trimite e-mailul, iar apelul nostru va returna un răspuns. Invocarea funcției locale va trimite un e-mail la adresa dvs. verificată.

 yarn sls invoke local --function staticSiteMailer --path data.json 
Răspunsul returnat de la SES.sendEmail când reușește.
Răspunsul returnat de la SES.sendEmail când reușește.

Returnarea unui răspuns de la Handler

Funcția noastră trimite un e-mail folosind linia de comandă, dar nu așa vor interacționa utilizatorii noștri cu ea. Trebuie să returnăm un răspuns la trimiterea formularului nostru AJAX. Dacă nu reușește, ar trebui să returnăm un statusCode corespunzător, precum și err.message . Când reușește, 200 statusCode este suficient, dar vom returna răspunsul mailului și în body. În staticSiteMailer construim datele noastre de răspuns și înlocuim funcția de apel invers sendEmail cu apel invers 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); }); };

Reapelul nostru Lambda returnează acum atât mesaje de succes, cât și mesaje de eșec de la SES.sendEmail . Construim răspunsul cu verificări dacă există o err , astfel încât răspunsul nostru să fie consecvent. Funcția de apel invers Lambda trece în sine null în câmpul argumentului de eroare și răspunsul ca al doilea. Dorim să transmitem erori în continuare, dar dacă Lambda în sine eșuează, apelul său va fi apelat implicit cu răspunsul la eroare.

În headers , va trebui să înlocuiți Access-Control-Allow-Origin cu propriul domeniu. Acest lucru va împiedica orice alte domenii să vă folosească serviciul și, potențial, să acumulați o factură AWS în numele dvs.! Și nu o acopăr în acest articol, dar este posibil să configurați Lambda pentru a vă folosi propriul domeniu. Va trebui să aveți un certificat SSL/TLS încărcat pe Amazon. Echipa Serverless Framework a scris un tutorial fantastic despre cum să faceți acest lucru.

Invocarea funcției locale va trimite acum un e-mail și va returna răspunsul corespunzător.

 yarn sls invoke local --function staticSiteMailer --path data.json 
Răspunsul returnat de la funcția noastră fără server, care conține răspunsul returnat SES.sendEmail în organism.
Răspunsul returnat de la funcția noastră fără server, care conține răspunsul returnat SES.sendEmail în organism.

Apelarea funcției Lambda din formular

Serviciul nostru este complet! Pentru a-l implementa, rulați yarn sls deploy -v . Odată implementat, veți obține o adresă URL care arată ceva de genul https://r4nd0mh45h.execute-api.us-east-1.amazonaws.com/dev/static-site-mailer pe care o puteți adăuga la acțiunea din formular. Apoi, creăm cererea AJAX și returnăm răspunsul utilizatorului.

 (() => { 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); } }; }; })();

În apelarea AJAX, verificăm codul de stare cu response.target.status . Dacă este altceva decât 200 , putem arăta utilizatorului un mesaj de eroare, în caz contrar anunță-i că mesajul a fost trimis. Deoarece Lambda returnează JSON stringificat, putem analiza corpul mesajului cu JSON.parse(response.target.response).message . Este deosebit de util să înregistrați eroarea.

Ar trebui să puteți trimite formularul în întregime de pe site-ul dvs. static!

Formularul static al site-ului, care trimite mesajul la punctul final Lambda și returnează un răspuns utilizatorului.
Formularul static al site-ului, care trimite mesajul la punctul final Lambda și returnează un răspuns utilizatorului.

Pasii urmatori

Adăugarea unui formular de contact la statica este ușoară cu Serverless Framework și AWS. Există loc de îmbunătățire în codul nostru, cum ar fi adăugarea de validare a formularelor cu un honeypot, prevenirea apelurilor AJAX pentru formulare nevalide și îmbunătățirea UX în cazul răspunsului, dar acest lucru este suficient pentru a începe. Puteți vedea unele dintre aceste îmbunătățiri în depozitul static de e-mail pe care l-am creat. Sper că v-am inspirat să încercați singur Serverless!