Membangun Formulir Kontak Tanpa Server Untuk Situs Statis Anda

Diterbitkan: 2022-03-10
Ringkasan cepat Dengan bantuan artikel ini, Anda akhirnya akan dapat mempelajari dasar-dasar Amazon Web Services (AWS) Lambda dan Simple Email Service (SES) API untuk membantu Anda membangun mailer situs statis Anda sendiri di Serverless Framework. Mari kita mulai!

Generator situs statis menyediakan alternatif yang cepat dan sederhana untuk Sistem Manajemen Konten (CMS) seperti WordPress. Tidak ada penyiapan server atau basis data, hanya proses pembuatan dan HTML, CSS, dan JavaScript sederhana. Sayangnya, tanpa server, mudah untuk mencapai batasnya dengan cepat. Misalnya, dalam menambahkan formulir kontak.

Dengan munculnya arsitektur tanpa server menambahkan formulir kontak ke situs statis Anda tidak perlu menjadi alasan untuk beralih ke CMS lagi. Dimungkinkan untuk mendapatkan yang terbaik dari kedua dunia: situs statis dengan back-end tanpa server untuk formulir kontak (yang tidak perlu Anda pertahankan). Mungkin yang terbaik dari semuanya, di situs dengan lalu lintas rendah, seperti portofolio, batas tinggi dari banyak penyedia tanpa server membuat layanan ini sepenuhnya gratis!

Dalam artikel ini, Anda akan mempelajari dasar-dasar Amazon Web Services (AWS) Lambda dan Simple Email Service (SES) API untuk membangun mailer situs statis Anda sendiri di Serverless Framework. Layanan lengkap akan mengambil data formulir yang dikirimkan dari permintaan AJAX, mencapai titik akhir Lambda, mengurai data untuk membangun parameter SES, mengirim alamat email, dan mengembalikan respons untuk pengguna kami. Saya akan memandu Anda melalui penyiapan Tanpa Server untuk pertama kalinya melalui penerapan. Perlu waktu kurang dari satu jam untuk menyelesaikannya, jadi mari kita mulai!

Bentuk situs statis, mengirim pesan ke titik akhir Lambda dan mengembalikan respons ke pengguna.
Bentuk situs statis, mengirim pesan ke titik akhir Lambda dan mengembalikan respons ke pengguna.
Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

Pengaturan

Ada prasyarat minimal untuk memulai dengan teknologi Tanpa Server. Untuk tujuan kami, ini hanyalah Lingkungan Node dengan Benang, Kerangka Tanpa Server, dan akun AWS.

Menyiapkan Proyek

Situs web Kerangka Tanpa Server. Berguna untuk instalasi dan dokumentasi.
Situs web Kerangka Tanpa Server. Berguna untuk instalasi dan dokumentasi.

Kami menggunakan Yarn untuk menginstal Kerangka Tanpa Server ke direktori lokal.

  1. Buat direktori baru untuk meng-host proyek.
  2. Arahkan ke direktori di antarmuka baris perintah Anda.
  3. Jalankan yarn init untuk membuat file package.json untuk proyek ini.
  4. Jalankan yarn add serverless untuk menginstal framework secara lokal.
  5. Jalankan yarn serverless create --template aws-nodejs --name static-site-mailer untuk membuat template layanan Node dan beri nama static-site-mailer .

Proyek kami telah disiapkan tetapi kami tidak akan dapat melakukan apa pun sampai kami menyiapkan layanan AWS kami.

Menyiapkan Akun Layanan Web Amazon, Kredensial, dan Layanan Email Sederhana

Halaman pendaftaran Amazon Web Services, yang mencakup tingkat gratis yang murah hati, memungkinkan proyek kami sepenuhnya gratis.
Halaman pendaftaran Amazon Web Services, yang mencakup tingkat gratis yang murah hati, memungkinkan proyek kami sepenuhnya gratis.

Kerangka Tanpa Server telah merekam panduan video untuk menyiapkan kredensial AWS, tetapi saya juga telah mencantumkan langkah-langkahnya di sini.

  1. Mendaftar untuk akun AWS atau masuk jika Anda sudah memilikinya.
  2. Di bilah pencarian AWS, cari "IAM".
  3. Di halaman IAM, klik "Pengguna" di bilah sisi, lalu tombol "Tambah pengguna".
  4. Pada halaman Tambahkan pengguna, beri nama pengguna – sesuatu seperti “tanpa server” sesuai. Centang "Akses terprogram" di bawah Jenis akses lalu klik berikutnya.
  5. Pada layar izin, klik tab “Lampirkan kebijakan yang ada secara langsung”, cari “AdministratorAccess” dalam daftar, centang, dan klik berikutnya.
  6. Pada layar tinjauan Anda akan melihat nama pengguna Anda, dengan "Akses program", dan "AdministratorAccess", lalu buat pengguna.
  7. Layar konfirmasi menunjukkan kepada pengguna “Access key ID” dan “Secret access key”, Anda akan memerlukan ini untuk menyediakan akses ke Serverless Framework. Di CLI Anda, ketikkan yarn sls config credentials --provider aws --key YOUR_ACCESS_KEY_ID --secret YOUR_SECRET_ACCESS_KEY , ganti YOUR_ACCESS_KEY_ID dan YOUR_SECRET_ACCESS_KEY dengan kunci pada layar konfirmasi.

Kredensial Anda telah dikonfigurasi sekarang, tetapi saat kita berada di konsol AWS, mari siapkan Layanan Email Sederhana.

  1. Klik Beranda Konsol di pojok kiri atas untuk pulang.
  2. Di halaman beranda, di bilah pencarian AWS, cari "Layanan Email Sederhana".
  3. Di halaman Beranda SES, klik "Alamat Email" di bilah sisi.
  4. Pada halaman daftar Alamat Email, klik tombol “Verifikasi Alamat Email Baru”.
  5. Di jendela dialog, ketik alamat email Anda lalu klik "Verifikasi Alamat Email Ini".
  6. Anda akan menerima email dalam beberapa saat yang berisi tautan untuk memverifikasi alamat. Klik tautan untuk menyelesaikan proses.

Sekarang setelah akun kita dibuat, mari kita intip file template Serverless.

Menyiapkan Kerangka Tanpa Server

Menjalankan serverless create dua file: handler.js yang berisi fungsi Lambda, dan serverless.yml yang merupakan file konfigurasi untuk seluruh Arsitektur Tanpa Server. Di dalam file konfigurasi, Anda dapat menentukan penangan sebanyak yang Anda inginkan, dan masing-masing akan dipetakan ke fungsi baru yang dapat berinteraksi dengan fungsi lain. Dalam proyek ini, kami hanya akan membuat satu handler, tetapi dalam Arsitektur Tanpa Server penuh, Anda akan memiliki beberapa dari berbagai fungsi layanan.

Struktur file default yang dihasilkan dari Serverless Framework yang berisi handler.js dan serverless.yml.
Struktur file default yang dihasilkan dari Serverless Framework yang berisi handler.js dan serverless.yml.

Di handler.js, Anda akan melihat satu fungsi yang diekspor bernama hello . Ini adalah fungsi utama (dan satu-satunya) saat ini. Ini, bersama dengan semua penangan Node, mengambil tiga parameter:

  • event
    Ini dapat dianggap sebagai data input untuk fungsi tersebut.
  • context object
    Ini berisi informasi runtime dari fungsi Lambda.
  • callback
    Parameter opsional untuk mengembalikan informasi ke pemanggil.
 // 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); };

Di bagian bawah hello , ada panggilan balik. Ini adalah argumen opsional untuk mengembalikan respons, tetapi jika tidak dipanggil secara eksplisit , itu akan secara implisit kembali dengan null . Panggilan balik membutuhkan dua parameter:

  • kesalahan kesalahan
    Untuk memberikan informasi kesalahan ketika Lambda itu sendiri gagal. Ketika Lambda berhasil, null harus diteruskan ke parameter ini.
  • Hasil objek
    Untuk menyediakan objek respons. Itu harus kompatibel dengan JSON.stringify . Jika ada parameter di bidang kesalahan, bidang ini diabaikan.

Situs statis kami akan mengirimkan data formulir kami di badan acara dan panggilan balik akan mengembalikan respons untuk dilihat pengguna kami.

Di serverless.yml Anda akan melihat nama layanan, informasi penyedia, dan fungsinya.

 # serverless.yml service: static-site-mailer provider: name: aws runtime: nodejs6.10 functions: hello: handler: handler.hello 
Bagaimana nama fungsi di serverless.yml dipetakan ke handler.js.
Bagaimana nama fungsi di serverless.yml dipetakan ke handler.js.

Perhatikan pemetaan antara fungsi hello dan handler? Kami dapat memberi nama file dan fungsi kami apa saja dan selama itu memetakan ke konfigurasi itu akan berfungsi. Mari kita ganti nama fungsi kita menjadi staticSiteMailer .

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

Fungsi Lambda memerlukan izin untuk berinteraksi dengan infrastruktur AWS lainnya. Sebelum kami dapat mengirim email, kami harus mengizinkan SES untuk melakukannya. Di serverless.yml, di bawah provider.iamRoleStatements tambahkan izin.

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

Karena kita memerlukan URL untuk tindakan formulir kita, kita perlu menambahkan peristiwa HTTP ke fungsi kita. Di serverless.yml kami membuat jalur, menentukan metode sebagai post , dan menyetel CORS ke true untuk keamanan.

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

File serverless.yml dan handler.js kami yang diperbarui akan terlihat seperti:

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

Arsitektur Tanpa Server kami telah disiapkan, jadi mari kita terapkan dan ujilah. Anda akan mendapatkan respons JSON sederhana.

 yarn sls deploy --verbose yarn sls invoke --function staticSiteMailer { "statusCode": 200, "body": "{\"message\":\"Go Serverless v1.0! Your function executed successfully!\",\"input\":{}}" } 
Respons balasan dari menjalankan fungsi tanpa server baru kami.
Respons balasan dari menjalankan fungsi tanpa server baru kami.

Membuat Formulir HTML

Input fungsi Lambda dan output formulir harus cocok, jadi sebelum kita membangun fungsi, kita akan membuat formulir dan menangkap outputnya. Kami membuatnya sederhana dengan bidang nama, email, dan pesan. Kami akan menambahkan tindakan formulir setelah kami menerapkan arsitektur tanpa server kami dan mendapatkan URL kami, tetapi kami tahu itu akan menjadi permintaan POST sehingga kami dapat menambahkannya. Di akhir formulir, kami menambahkan tag paragraf untuk ditampilkan pesan tanggapan kepada pengguna yang akan kami perbarui pada panggilan balik pengiriman.

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

Untuk menangkap output, kami menambahkan penangan pengiriman ke formulir, mengubah parameter formulir kami menjadi objek, dan mengirim JSON string ke fungsi Lambda kami. Dalam fungsi Lambda kami menggunakan JSON.parse() untuk membaca data kami. Atau, Anda bisa menggunakan jQuery's Serialize atau query-string untuk mengirim dan mengurai parameter formulir sebagai string kueri tetapi JSON.stringify() dan JSON.parse() adalah asli.

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

Silakan dan kirimkan formulir Anda lalu ambil output konsol. Kami akan menggunakannya dalam fungsi Lambda kami selanjutnya.

Menangkap data formulir di log konsol.
Menangkap data formulir di log konsol.

Menjalankan Fungsi Lambda

Terutama selama pengembangan, kita perlu menguji fungsi kita melakukan apa yang kita harapkan. Kerangka Tanpa Server menyediakan perintah invoke local invoke memicu fungsi Anda masing-masing dari lingkungan langsung dan pengembangan . Kedua perintah memerlukan nama fungsi yang dilewati, dalam kasus kami staticSiteMailer .

 yarn sls invoke local --function staticSiteMailer

Untuk meneruskan data tiruan ke dalam fungsi kita, buat file baru bernama data.json dengan output konsol yang ditangkap di bawah kunci body di dalam objek JSON. Seharusnya terlihat seperti:

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

Untuk memanggil fungsi dengan data lokal, berikan argumen --path bersama dengan path ke file.

 yarn sls invoke local --function staticSiteMailer --path data.json 
Respons pengembalian yang diperbarui dari fungsi tanpa server kami saat kami meneruskannya data JSON.
Respons pengembalian yang diperbarui dari fungsi tanpa server kami saat kami meneruskannya data JSON.

Anda akan melihat respons yang serupa dengan sebelumnya, tetapi kunci input akan berisi acara yang kami olok-olok. Mari gunakan data tiruan kami untuk mengirim email menggunakan Layanan Email Sederhana!

Mengirim Email Dengan Layanan Email Sederhana

Kita akan mengganti fungsi staticSiteMailer dengan panggilan ke fungsi private sendEmail . Untuk saat ini Anda dapat mengomentari atau menghapus kode template dan menggantinya dengan:

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

Pertama, kami mengurai event.body untuk menangkap data formulir, lalu kami meneruskannya ke fungsi sendEmail pribadi. sendEmail bertanggung jawab untuk mengirim email, dan fungsi panggilan balik akan mengembalikan respons yang gagal atau berhasil dengan err atau data . Dalam kasus kami, kami dapat dengan mudah mencatat kesalahan atau data karena kami akan segera menggantinya dengan panggilan balik Lambda.

Amazon menyediakan SDK yang nyaman, aws-sdk , untuk menghubungkan layanan mereka dengan fungsi Lambda. Banyak dari layanan mereka, termasuk SES, adalah bagian darinya. Kami menambahkannya ke proyek dengan yarn add aws-sdk dan mengimpornya ke bagian atas file handler.

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

Dalam fungsi sendEmail pribadi kami, kami membangun parameter SES.sendEmail dari data formulir yang diuraikan dan menggunakan panggilan balik untuk mengembalikan respons ke pemanggil. Parameter memerlukan yang berikut ini sebagai objek:

  • Sumber
    Alamat email yang dikirim SES dari .
  • BalasKeAlamat
    Array alamat email ditambahkan ke balasan ke bidang di email.
  • Tujuan
    Objek yang harus berisi setidaknya satu ToAddresses , CcAddresses , atau BccAddresses . Setiap bidang mengambil larik alamat email yang sesuai dengan bidang ke , cc , dan bcc masing-masing.
  • Pesan
    Sebuah objek yang berisi Tubuh dan Subjek .

Karena formData adalah objek, kami dapat memanggil bidang formulir kami secara langsung seperti formData.message , membangun parameter kami, dan mengirimkannya. Kami meneruskan email terverifikasi SES Anda ke Source and Destination.ToAddresses . Selama email diverifikasi, Anda dapat meneruskan apa pun di sini, termasuk alamat email yang berbeda. Kami mencabut reply_to , message , dan name dari objek formData kami untuk mengisi bidang ReplyToAddresses dan 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 akan mengirimkan email tersebut dan callback kami akan membalasnya. Memanggil fungsi lokal akan mengirim email ke alamat terverifikasi Anda.

 yarn sls invoke local --function staticSiteMailer --path data.json 
Tanggapan kembali dari SES.sendEmail ketika berhasil.
Tanggapan kembali dari SES.sendEmail ketika berhasil.

Mengembalikan Tanggapan Dari Handler

Fungsi kami mengirim email menggunakan baris perintah, tapi bukan itu cara pengguna kami berinteraksi dengannya. Kami perlu mengembalikan tanggapan atas pengiriman formulir AJAX kami. Jika gagal, kita harus mengembalikan statusCode yang sesuai serta err.message . Ketika berhasil, 200 statusCode sudah cukup, tetapi kami juga akan mengembalikan respons mailer di badan. Di staticSiteMailer kami membangun data respons kami dan mengganti fungsi panggilan balik sendEmail kami dengan panggilan balik 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); }); };

Callback Lambda kami sekarang mengembalikan pesan sukses dan gagal dari SES.sendEmail . Kami membangun respons dengan memeriksa apakah ada err sehingga respons kami konsisten. Fungsi panggilan balik Lambda itu sendiri melewati null di bidang argumen kesalahan dan respons sebagai yang kedua. Kami ingin meneruskan kesalahan, tetapi jika Lambda itu sendiri gagal, panggilan baliknya akan dipanggil secara implisit dengan respons kesalahan.

Di headers , Anda harus mengganti Access-Control-Allow-Origin dengan domain Anda sendiri. Ini akan mencegah domain lain menggunakan layanan Anda dan berpotensi membebani tagihan AWS atas nama Anda! Dan saya tidak membahasnya dalam artikel ini, tetapi dimungkinkan untuk menyiapkan Lambda untuk menggunakan domain Anda sendiri. Anda harus memiliki sertifikat SSL/TLS yang diunggah ke Amazon. Tim Serverless Framework menulis tutorial fantastis tentang cara melakukannya.

Memanggil fungsi lokal sekarang akan mengirim email dan mengembalikan respons yang sesuai.

 yarn sls invoke local --function staticSiteMailer --path data.json 
Respons balik dari fungsi tanpa server kami, yang berisi respons pengembalian SES.sendEmail di badan.
Respons balik dari fungsi tanpa server kami, yang berisi respons pengembalian SES.sendEmail di badan.

Memanggil Fungsi Lambda Dari Formulir

Layanan kami selesai! Untuk menyebarkannya, jalankan yarn sls deploy -v . Setelah diterapkan, Anda akan mendapatkan URL yang terlihat seperti https://r4nd0mh45h.execute-api.us-east-1.amazonaws.com/dev/static-site-mailer yang dapat Anda tambahkan ke tindakan formulir. Selanjutnya, kami membuat permintaan AJAX dan mengembalikan respons ke pengguna.

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

Dalam panggilan balik AJAX, kami memeriksa kode status dengan response.target.status . Jika itu selain 200 , kami dapat menampilkan pesan kesalahan kepada pengguna, jika tidak, beri tahu mereka bahwa pesan telah dikirim. Karena Lambda kami mengembalikan string JSON, kami dapat mengurai pesan isi dengan JSON.parse(response.target.response).message . Ini sangat berguna untuk mencatat kesalahan.

Anda harus dapat mengirimkan formulir Anda sepenuhnya dari situs statis Anda!

Bentuk situs statis, mengirim pesan ke titik akhir Lambda dan mengembalikan respons ke pengguna.
Bentuk situs statis, mengirim pesan ke titik akhir Lambda dan mengembalikan respons ke pengguna.

Langkah selanjutnya

Menambahkan formulir kontak ke statis Anda mudah dilakukan dengan Serverless Framework dan AWS. Ada ruang untuk perbaikan dalam kode kami, seperti menambahkan validasi formulir dengan honeypot, mencegah panggilan AJAX untuk formulir yang tidak valid dan meningkatkan UX jika respons, tetapi ini cukup untuk memulai. Anda dapat melihat beberapa peningkatan ini dalam repo mailer situs statis yang saya buat. Saya harap saya telah menginspirasi Anda untuk mencoba sendiri Tanpa Server!