Créer un formulaire de contact sans serveur pour votre site statique
Publié: 2022-03-10Les générateurs de sites statiques offrent une alternative simple et rapide aux systèmes de gestion de contenu (CMS) comme WordPress. Il n'y a pas de configuration de serveur ou de base de données, juste un processus de construction et de simples HTML, CSS et JavaScript. Malheureusement, sans serveur, il est facile d'atteindre rapidement ses limites. Par exemple, en ajoutant un formulaire de contact.
Avec l'essor de l'architecture sans serveur, l'ajout d'un formulaire de contact à votre site statique n'a plus besoin d'être la raison de passer à un CMS. Il est possible d'obtenir le meilleur des deux mondes : un site statique avec un back-end sans serveur pour le formulaire de contact (que vous n'avez pas besoin de maintenir). Peut-être le meilleur de tous, dans les sites à faible trafic, comme les portefeuilles, les limites élevées de nombreux fournisseurs sans serveur rendent ces services totalement gratuits !
Dans cet article, vous apprendrez les bases des API Amazon Web Services (AWS) Lambda et Simple Email Service (SES) pour créer votre propre messagerie de site statique sur Serverless Framework. Le service complet prendra les données de formulaire soumises à partir d'une requête AJAX, frappera le point de terminaison Lambda, analysera les données pour créer les paramètres SES, enverra l'adresse e-mail et renverra une réponse à nos utilisateurs. Je vais vous guider dans la configuration de Serverless pour la première fois via le déploiement. Cela devrait prendre moins d'une heure, alors commençons !
Mise en place
Il existe des prérequis minimaux pour démarrer avec la technologie sans serveur. Pour nos besoins, il s'agit simplement d'un environnement de nœud avec Yarn, du Serverless Framework et d'un compte AWS.
Configuration du projet
Nous utilisons Yarn pour installer le Serverless Framework dans un répertoire local.
- Créez un nouveau répertoire pour héberger le projet.
- Accédez au répertoire dans votre interface de ligne de commande.
- Exécutez
yarn init
pour créer un fichierpackage.json
pour ce projet. - Exécutez
yarn add serverless
pour installer le framework localement. - Exécutez
yarn serverless create --template aws-nodejs --name static-site-mailer
pour créer un modèle de service Node et nommez-lestatic-site-mailer
.
Notre projet est configuré mais nous ne pourrons rien faire tant que nous n'aurons pas configuré nos services AWS.
Configuration d'un compte Amazon Web Services, d'informations d'identification et d'un service de messagerie simple
Le Serverless Framework a enregistré une vidéo pas à pas pour configurer les informations d'identification AWS, mais j'ai également répertorié les étapes ici.
- Créez un compte AWS ou connectez-vous si vous en avez déjà un.
- Dans la barre de recherche AWS, recherchez « IAM ».
- Sur la page IAM, cliquez sur "Utilisateurs" dans la barre latérale, puis sur le bouton "Ajouter un utilisateur".
- Sur la page Ajouter un utilisateur, donnez un nom à l'utilisateur - quelque chose comme "sans serveur" est approprié. Cochez « Accès par programme » sous Type d'accès, puis cliquez sur Suivant.
- Sur l'écran des autorisations, cliquez sur l'onglet "Attacher directement les stratégies existantes", recherchez "AdministratorAccess" dans la liste, cochez-le et cliquez sur suivant.
- Sur l'écran de révision, vous devriez voir votre nom d'utilisateur, avec "Accès programmatique" et "Accès administrateur", puis créez l'utilisateur.
- L'écran de confirmation affiche l'utilisateur "Access key ID" et "Secret access key", vous en aurez besoin pour fournir l'accès au Serverless Framework. Dans votre CLI, tapez
yarn sls config credentials --provider aws --key YOUR_ACCESS_KEY_ID --secret YOUR_SECRET_ACCESS_KEY
, en remplaçantYOUR_ACCESS_KEY_ID
etYOUR_SECRET_ACCESS_KEY
par les clés sur l'écran de confirmation.
Vos informations d'identification sont maintenant configurées, mais pendant que nous sommes dans la console AWS, configurons Simple Email Service.
- Cliquez sur Console Home dans le coin supérieur gauche pour revenir à la maison.
- Sur la page d'accueil, dans la barre de recherche AWS, recherchez « Simple Email Service ».
- Sur la page d'accueil de SES, cliquez sur "Adresses e-mail" dans la barre latérale.
- Sur la page de liste des adresses e-mail, cliquez sur le bouton "Vérifier une nouvelle adresse e-mail".
- Dans la fenêtre de dialogue, tapez votre adresse e-mail puis cliquez sur "Vérifier cette adresse e-mail".
- Vous recevrez un e-mail dans quelques instants contenant un lien pour vérifier l'adresse. Cliquez sur le lien pour terminer le processus.
Maintenant que nos comptes sont créés, jetons un coup d'œil aux fichiers de modèle Serverless.
Configuration du framework sans serveur
L'exécution serverless create
crée deux fichiers : handler.js qui contient la fonction Lambda et serverless.yml qui est le fichier de configuration pour l'ensemble de l'architecture sans serveur. Dans le fichier de configuration, vous pouvez spécifier autant de gestionnaires que vous le souhaitez, et chacun correspondra à une nouvelle fonction qui peut interagir avec d'autres fonctions. Dans ce projet, nous ne créerons qu'un seul gestionnaire, mais dans une architecture sans serveur complète, vous auriez plusieurs des différentes fonctions du service.
Dans handler.js, vous verrez une seule fonction exportée nommée hello
. C'est actuellement la fonction principale (et unique). Il, avec tous les gestionnaires de nœuds, prend trois paramètres :
-
event
Cela peut être considéré comme les données d'entrée de la fonction. -
context object
Il contient les informations d'exécution de la fonction Lambda. -
callback
Un paramètre facultatif pour renvoyer des informations à l'appelant.
// 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); };
Au bas de hello
, il y a un rappel. C'est un argument facultatif pour retourner une réponse, mais s'il n'est pas explicitement appelé, il retournera implicitement avec null
. Le rappel prend deux paramètres :
- Erreur erreur
Pour fournir des informations d'erreur en cas d'échec de Lambda lui-même. Lorsque Lambda réussit,null
doit être transmis à ce paramètre. - Résultat d'objet
Pour fournir un objet de réponse. Il doit être compatibleJSON.stringify
. S'il y a un paramètre dans le champ d'erreur, ce champ est ignoré.
Notre site statique enverra nos données de formulaire dans le corps de l'événement et le rappel renverra une réponse que notre utilisateur pourra voir.
Dans serverless.yml, vous verrez le nom du service, les informations sur le fournisseur et les fonctions.
# serverless.yml service: static-site-mailer provider: name: aws runtime: nodejs6.10 functions: hello: handler: handler.hello
Remarquez le mappage entre la fonction hello et le gestionnaire ? Nous pouvons nommer notre fichier et fonctionner n'importe quoi et tant qu'il correspond à la configuration, cela fonctionnera. Renommez notre fonction en staticSiteMailer
.
# serverless.yml functions: staticSiteMailer: handler: handler.staticSiteMailer
// handler.js module.exports.staticSiteMailer = (event, context, callback) => { ... };
Les fonctions Lambda ont besoin d'une autorisation pour interagir avec d'autres infrastructures AWS. Avant de pouvoir envoyer un e-mail, nous devons autoriser SES à le faire. Dans serverless.yml, sous provider.iamRoleStatements
, ajoutez l'autorisation.
# serverless.yml provider: name: aws runtime: nodejs6.10 iamRoleStatements: - Effect: "Allow" Action: - "ses:SendEmail" Resource: ["*"]
Comme nous avons besoin d'une URL pour notre action de formulaire, nous devons ajouter des événements HTTP à notre fonction. Dans serverless.yml, nous créons un chemin, spécifions la méthode post
et définissons CORS sur true pour la sécurité.
functions: staticSiteMailer: handler: handler.staticSiteMailer events: - http: method: post path: static-site-mailer cors: true
Nos fichiers serverless.yml et handler.js mis à jour devraient ressembler à :
# 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); };
Notre architecture sans serveur est configurée, alors déployons-la et testons-la. Vous obtiendrez une réponse JSON simple.
yarn sls deploy --verbose yarn sls invoke --function staticSiteMailer { "statusCode": 200, "body": "{\"message\":\"Go Serverless v1.0! Your function executed successfully!\",\"input\":{}}" }
Création du formulaire HTML
L'entrée de notre fonction Lambda et la sortie du formulaire doivent correspondre, donc avant de créer la fonction, nous allons créer le formulaire et capturer sa sortie. Nous gardons les choses simples avec les champs de nom, d'e-mail et de message. Nous ajouterons l'action de formulaire une fois que nous aurons déployé notre architecture sans serveur et obtenu notre URL, mais nous savons qu'il s'agira d'une requête POST afin que nous puissions l'ajouter. À la fin du formulaire, nous ajoutons une balise de paragraphe pour afficher messages de réponse à l'utilisateur que nous mettrons à jour lors du rappel de soumission.
<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>
Pour capturer la sortie, nous ajoutons un gestionnaire de soumission au formulaire, transformons nos paramètres de formulaire en objet et envoyons du JSON sous forme de chaîne à notre fonction Lambda. Dans la fonction Lambda, nous utilisons JSON.parse()
pour lire nos données. Alternativement, vous pouvez utiliser Serialize ou query-string de jQuery pour envoyer et analyser les paramètres de formulaire en tant que chaîne de requête, mais JSON.stringify()
et JSON.parse()
sont natifs.
(() => { 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)); }; })();
Allez-y et soumettez votre formulaire, puis capturez la sortie de la console. Nous l'utiliserons ensuite dans notre fonction Lambda.
Appel des fonctions Lambda
Surtout pendant le développement, nous devons tester que notre fonction fait ce que nous attendons. Le invoke
Framework fournit la commande d'appel et invoke local
pour déclencher votre fonction à partir d'environnements en direct et de développement respectivement. Les deux commandes nécessitent le nom de la fonction transmis, dans notre cas staticSiteMailer
.
yarn sls invoke local --function staticSiteMailer
Pour transmettre des données fictives à notre fonction, créez un nouveau fichier nommé data.json
avec la sortie de la console capturée sous une clé de body
dans un objet JSON. Cela devrait ressembler à quelque chose comme :
// data.json { "body": "{\"name\": \"Sender Name\",\"reply_to\": \"[email protected]\",\"message\": \"Sender message\"}" }
Pour invoquer la fonction avec les données locales, transmettez l'argument --path
avec le chemin d'accès au fichier.
yarn sls invoke local --function staticSiteMailer --path data.json
Vous verrez une réponse similaire à avant, mais la clé input
contiendra l'événement dont nous nous sommes moqués. Utilisons nos données fictives pour envoyer un e-mail à l'aide de Simple Email Service !
Envoi d'un e-mail avec un service de messagerie simple
Nous allons remplacer la fonction staticSiteMailer
par un appel à une fonction privée sendEmail
. Pour l'instant, vous pouvez commenter ou supprimer le code du modèle et le remplacer par :
// 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); } }); };
Tout d'abord, nous event.body
pour capturer les données du formulaire, puis nous le transmettons à une fonction sendEmail
privée. sendEmail
est responsable de l'envoi de l'e-mail, et la fonction de rappel renverra une réponse d'échec ou de succès avec err
ou data
. Dans notre cas, nous pouvons simplement consigner l'erreur ou les données puisque nous les remplacerons par le rappel Lambda dans un instant.
Amazon fournit un SDK pratique, aws-sdk
, pour connecter leurs services aux fonctions Lambda. Beaucoup de leurs services, dont SES, en font partie. Nous l'ajoutons au projet avec yarn add aws-sdk
et l'importons en haut du fichier de gestionnaire.
// handler.js const AWS = require('aws-sdk'); const SES = new AWS.SES();
Dans notre fonction privée sendEmail
, nous construisons les paramètres SES.sendEmail
à partir des données de formulaire analysées et utilisons le rappel pour renvoyer une réponse à l'appelant. Les paramètres nécessitent les éléments suivants en tant qu'objet :
- La source
L'adresse e-mail à partir de laquelle SES envoie . - Répondre aux adresses
Un tableau d'adresses e-mail ajoutées à la réponse au champ dans l'e-mail. - Destination
Un objet qui doit contenir au moins un ToAddresses , CcAddresses ou BccAddresses . Chaque champ prend un tableau d'adresses e-mail qui correspondent respectivement aux champs to , cc et bcc . - Un message
Un objet qui contient le Body et le Subject .
Puisque formData
est un objet, nous pouvons appeler nos champs de formulaire directement comme formData.message
, créer nos paramètres et les envoyer. Nous transmettons votre e-mail vérifié par SES à Source
et Destination.ToAddresses
. Tant que l'e-mail est vérifié, vous pouvez transmettre n'importe quoi ici, y compris différentes adresses e-mail. Nous cueillons notre reply_to
, message
et name
notre objet formData
pour remplir les champs ReplyToAddresses
et 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
enverra l'e-mail et notre rappel renverra une réponse. L'appel de la fonction locale enverra un e-mail à votre adresse vérifiée.
yarn sls invoke local --function staticSiteMailer --path data.json
Renvoyer une réponse du gestionnaire
Notre fonction envoie un e-mail en utilisant la ligne de commande, mais ce n'est pas ainsi que nos utilisateurs interagiront avec elle. Nous devons renvoyer une réponse à notre soumission de formulaire AJAX. Si cela échoue, nous devrions retourner un statusCode
approprié ainsi que le err.message
. Lorsqu'il réussit, le statusCode
200
est suffisant, mais nous renverrons également la réponse de l'expéditeur dans le corps. Dans staticSiteMailer
nous créons nos données de réponse et remplaçons notre fonction de rappel sendEmail
par le rappel 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); }); };
Notre rappel Lambda renvoie désormais les messages de réussite et d'échec de SES.sendEmail
. Nous construisons la réponse en vérifiant si err
est présent afin que notre réponse soit cohérente. La fonction de rappel Lambda elle-même transmet null
dans le champ d'argument d'erreur et la réponse en second. Nous voulons transmettre les erreurs, mais si Lambda lui-même échoue, son rappel sera implicitement appelé avec la réponse d'erreur.
Dans les en- headers
, vous devrez remplacer Access-Control-Allow-Origin
par votre propre domaine. Cela empêchera tout autre domaine d'utiliser votre service et d'accumuler potentiellement une facture AWS à votre nom ! Et je ne le couvre pas dans cet article, mais il est possible de configurer Lambda pour utiliser votre propre domaine. Vous aurez besoin d'avoir un certificat SSL/TLS chargé sur Amazon. L'équipe Serverless Framework a écrit un didacticiel fantastique sur la façon de procéder.
L'appel de la fonction locale enverra désormais un e-mail et renverra la réponse appropriée.
yarn sls invoke local --function staticSiteMailer --path data.json
Appel de la fonction Lambda à partir du formulaire
Notre service est complet ! Pour le déployer, exécutez yarn sls deploy -v
. Une fois déployé, vous obtiendrez une URL qui ressemble à quelque chose comme https://r4nd0mh45h.execute-api.us-east-1.amazonaws.com/dev/static-site-mailer
que vous pouvez ajouter à l'action du formulaire. Ensuite, nous créons la requête AJAX et renvoyons la réponse à l'utilisateur.
(() => { 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); } }; }; })();
Dans le rappel AJAX, nous vérifions le code d'état avec response.target.status
. Si c'est autre chose que 200
, nous pouvons afficher un message d'erreur à l'utilisateur, sinon faites-lui savoir que le message a été envoyé. Étant donné que notre Lambda renvoie un JSON sous forme de chaîne, nous pouvons analyser le corps du message avec JSON.parse(response.target.response).message
. Il est particulièrement utile de consigner l'erreur.
Vous devriez pouvoir soumettre votre formulaire entièrement à partir de votre site statique !
Prochaines étapes
Ajouter un formulaire de contact à votre statique est facile avec Serverless Framework et AWS. Il y a place à amélioration dans notre code, comme l'ajout d'une validation de formulaire avec un pot de miel, la prévention des appels AJAX pour les formulaires invalides et l'amélioration de l'UX si la réponse, mais cela suffit pour commencer. Vous pouvez voir certaines de ces améliorations dans le référentiel de messagerie de site statique que j'ai créé. J'espère que je vous ai inspiré à essayer Serverless vous-même !