Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> ซอฟต์แวร์ >> อีเมล

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

ผมเพิ่งสร้าง Landing Page ให้ลูกค้าที่อยากรับอีเมลผ่านเว็บไซต์โดยไม่เปิดเผยอีเมลของตน

ซื่อสัตย์ ผมไม่เคย Implement ฟังก์ชันนี้เองมาก่อน เคยใช้แค่ปุ่ม Contact Us พร้อม Anchor Tag และ mailto ใน href แบบนี้:

<button> <a href="mailto:myemail@example.com">Contact Me</a> </button> 

แต่วิธีนี้มีข้อไม่สะดวก 2 ประการ:

  1. บังคับทั้งสองฝ่าย (ผู้ส่งและผู้รับ) แชร์อีเมลกัน แม้ OK บางคน แต่ไม่เหมาะสำหรับคนห่วง Privacy
  2. ผู้เยี่ยมชมกดลิงก์บังคับเปิด Default Mail Program บนอุปกรณ์ อาจทำให้หงุดหงิด: ถ้าใช้ Public Computer? ไม่ได้ Login? หรือแค่ไม่อยากใช้ Mail Program? ใช่ เทคนิค Copy Email ไปส่งจาก Browser/ที่ Login อยู่ก็ได้ แต่นี่คือ Extra Steps ที่อาจทำให้ User ไม่อยากส่งข้อความ และธุรกิจเสียโอกาส

เพราะฉะนั้น เลือกใช้ Email Form ให้ User พิมพ์ข้อความ กด Submit ส่งอีเมลให้เจ้าของเว็บโดยไม่ต้องออกจากเว็บไซต์

Google หน่อย พบ Third-party Tools/Widgets ที่ Embed ได้ แต่ส่วนใหญ่ Branded และต้องจ่าย Subscription สำหรับ Customization เต็มรูปแบบ

และเว้นแต่จะใช้ CMS อย่าง WordPress ที่มี Plugin Built-in ทำได้ นี่คือ Recurring Cost ที่ไม่สะดวก

ผมเลือกเขียน Feature เอง เพื่อ Control เต็มที่

ในคู่มือนี้ ผมจะสร้างขั้นตอนที่ทำ จริงใช้ HTML และ AWS Services

The HTML Form

ทำให้เรียบง่าย HTML Form พื้นฐาน ไม่มี CSS แค่ทดสอบ Functionality

<h2>Contact Us</h2> <form> <label for="name">Name:</label> <input name="name" type="text"/><br/><br/> <label for="email">Email:</label> <input name="email" type="email"/><br/><br/> <label for="name">Message:</label> <textarea name="message"></textarea><br/><br/> <input type="submit"/> <div> <p id="result-text"></p> </div> </form> 
รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway
ไม่มีอะไรฟูมฟัก...

ตอนนี้จัดการ Submit ด้วย JavaScript

const form = document.querySelector('form') form.addEventListener('submit', event => { // prevent the form submit from refreshing the page event.preventDefault() const { name, email, message } = event.target console.log('Name: ', name.value) console.log('email: ', email.value) console.log('Message: ', message.value) }) 

ตอนนี้มี Form รับ Input และ JS แค่ Log ผลลัพธ์ใน Console

ทิ้งไว้ก่อน ไปสร้าง Backend Services ที่รับ Form Data และส่งอีเมลพร้อมข้อมูลนั้น

The Backend Overview

ลงลึก AWS และ Services ที่จะใช้และวิธีใช้

เหมือนในหัวข้อ เราจะใช้ AWS Lambda และ Simple Email Service (SES) SES คือ Serverless Messaging Service ส่งอีเมลเมื่อถูก Invoke Lambda ให้เขียน Server-side Code รันตอบสนอง Events

เราจะใช้ API Gateway ให้ Invoke Lambda Functions ผ่าน HTTP

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

ในกรณีนี้ เมื่อ Form ถูก Submit Workflow จะเป็น:

  1. Browser (JavaScript) ส่ง POST Request พร้อม Form Data ใน Request Body ไป Endpoint URL จาก AWS API Gateway
  2. API Gateway Validate Request แล้ว Trigger Lambda Function ที่รับ Event Parameter API Gateway ใส่ Form Data ใน Body Property ของ Event Parameter
  3. Lambda Function ดึง Data จาก Event Body สร้าง Email Body และ Recipients ใช้ AWS SDK เรียก SES พร้อม Email Data
  4. SES ได้ sendMail Request แปลง Email Data เป็น Text Email จริง ส่งไป Recipient ผ่าน Mail Servers ของ AWS

ส่งอีเมลเสร็จ Browser ได้ Response Status 200 และ Success Message หากขั้นตอนไหนใน AWS Cloud Fail Response จะเป็น Status 500

ขั้นตอนที่ 1: ตั้งค่า SES

เราจะตั้งค่าแต่ละขั้นตอนย้อนกลับ เริ่มจาก SES ที่ง่ายที่สุด

ใน AWS Console ไป SES Service → คลิก Email Addresses ใน Side Menu → กด Verify a New Email Address

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

ใน Dialog ที่เปิด ใส่อีเมลที่ต้องการให้ SES ใช้เป็น Sender ตอนส่งอีเมล

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

จะส่งอีเมลไปที่อีเมลนั้น พร้อมลิงก์คลิก Verify นี่คือวิธี AWS รู้ว่าเจ้าของอีเมลยินยอมให้ใช้ Address นี้เป็น Sender

จนกว่าจะ Verify SES Dashboard จะแสดง Verification Status เป็น Pending

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

เมื่อเจ้าของอีเมลเปิดอีเมลจาก AWS และคลิก Verify Link Status ควรเปลี่ยนเป็น Verified (Refresh หน้าเพื่อดู)

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

แค่นี้ก็จบ SES แล้ว สามารถทดสอบได้โดยเลือก Verified Email ในรายการ กด Send a Test Email ใส่ Recipient, Subject, Message ส่งได้

อีเมลที่ส่งจะถูก Sign โดย AWS Servers และ Verified Address จะเป็น Sender ควรเป็นแบบนี้:

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

ขั้นตอนที่ 2: ตั้งค่า Lambda

นี่คือส่วนสนุกที่สุด สร้าง Function ที่รับ Form Data และเรียก SES

ความสวยงามของ Lambda Functions: ไม่ต้องกังวลรัน Backend Code บน Server 24/7 และ Maintain Server มัน Serverless

แต่ไม่ใช่ว่าไม่มี Server AWS จัดการใต้ฝาฟ้า ให้โฟกัสเขียน Code เท่านั้น นอกจากนี้ Billing เฉพาะจำนวนครั้งที่ Function ถูกเรียก และเวลาที่ Execute ราคาถูกมาก!

สร้าง IAM Role และ Config

ก่อนเขียน Lambda Function ต้องสร้าง IAM Role Attach ให้ Function และให้ Permissions (Policies ใน AWS) เรียก SES

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

จาก AWS Console ไป IAM Service → คลิก Policies ใน Side Menu → กด Create Policy

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

ในหน้าสร้าง Policy ไป Tab JSON วาง Permissions นี้ แล้ว Next

{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "ses:SendEmail", "ses:SendRawEmail" ], "Resource": "*" } ] } 

หน้าที่ 3 ตั้งชื่อ Policy กด Create Policy

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

ตอนนี้สร้าง IAM Role Attach ให้ Lambda และ Link กับ Policy ที่สร้างไป

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

จาก IAM Side Menu คลิก Roles → กด Create role

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

ในหน้าสร้าง Role ตรวจสอบ Type เป็น AWS Service เลือก Lambda Case → กด Next: Permissions

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

หน้าต่อไป ค้นหา Policy ที่สร้างก่อนหน้าจากชื่อ เลือก → Next

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

หน้า Review ตั้งชื่อ Role ที่จำง่าย → กด Create role

ตอนนี้สร้าง Lambda Function ใหม่ ไป Lambda Service Dashboard กด Create Function

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

ในหน้าสร้าง Function ตั้งชื่อ เลือก Author from scratch เลือก Runtime Node.js

ภายใต้ Change default execution role เลือก Use an existing role แล้วเลือก Role ที่สร้างจาก Dropdown Existing role

กด Create function

เขียนโค้ดและทดสอบ

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

ใน Editor เปิดไฟล์ index.js (ไฟล์ที่รันเมื่อ Lambda ถูกเรียก) แทนที่เนื้อหาด้วยโค้ดนี้:

const aws = require("aws-sdk"); const ses = new aws.SES({ region: "us-east-1" }); exports.handler = async function (event) { console.log('EVENT: ', event) const params = { Destination: { ToAddresses: ["your@email.com"], }, Message: { Body: { Text: { Data: `Hello from Lambda!` }, }, Subject: { Data: `Message from AWS Lambda` }, }, Source: "your@email.com", }; return ses.sendEmail(params).promise() }; 

สังเกตบรรทัดที่ 2 ใช้ AWS SDK สร้าง SES Instance เหตุที่เลือก us-east-1 คือ Region ที่ผม Verify Email อย่าลืมเปลี่ยน Email และใช้ AWS Region ที่ Verify Email ของคุณ

ทดสอบ Function: กด Deploy แล้วกด Test → Configure test event จะเปิด Dialog สร้าง Test Event ใหม่

ใน Test Event Body Editor ใส่ JSON นี้ (เลียนแบบ Request จาก Browser) แล้ว Create

{ "body": { "senderName": "Namo", "senderEmail": "namo@trains.com", "message": "I love trains!" } } 

กด Test จะรัน Test ที่สร้าง ควรเปิด Tab ใหม่ใน Editor แสดง Logs จากการรัน Function ประมาณนี้:

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

สังเกต Event Object ที่ Log แสดงอยู่ภายใต้ Function Logs พร้อม Body Data จาก Test Event

Test นี้ควรส่งอีเมลไป Inbox ผมด้วย — เช็คดู

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

ถูกต้อง ตามคาด เกิดขึ้นทันทีหลังรัน Test

ตอนนี้ปรับโค้ด Function ให้ข้อความมีความหมายมากขึ้นจาก Test Data

const aws = require("aws-sdk"); const ses = new aws.SES({ region: "us-east-1" }); exports.handler = async function (event) { console.log('EVENT: ', event) // Extract properties from event body const { senderEmail, senderName, message } = JSON.parse(event.body) const params = { Destination: { ToAddresses: ["the.benhawy@gmail.com"], }, // Interpolate data in strings to send Message: { Body: { Text: { Data: `You just got a message from ${senderName} - ${senderEmail}: ${message}` }, }, Subject: { Data: `Message from ${senderName}` }, }, Source: "the.benhawy@gmail.com", }; return ses.sendEmail(params).promise(); }; 

สำคัญ: เมื่อ API Gateway เรียก Function จะส่ง String ไปใน Event Body เพราะฉะนั้นใช้ JSON.parse บน event.body เพื่อแปลงเป็น JSON และดึง Sender Email, Name, Message แล้วนำไป Interpolate ใน Email Body และ Subject

หากลอง Test จะ Error เพราะ Test ส่ง JSON Object ไป event.body แต่เรา JSON.parse บน JSON ทำให้ Error ใน JavaScript

น่าเสียดาย Test Editor ไม่อนุญาตส่ง String ไป event.body ต้องทดสอบที่อื่นต่อ

ขั้นตอนที่ 3: ตั้งค่า API Gateway

บริการ AWS ตัวสุดท้ายคือ API Gateway ให้ Browser ส่ง HTTP Request ไป Lambda Function ที่สร้าง

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

โดยไม่ต้องออกจากหน้า Lambda Function ขยาย Section Function overview กด Add trigger

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

เลือก API Gateway จาก Dropdown, HTTP API as API Type, Open as Security Mechanism, ติ๊ก CORS Checkbox → กด Add

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

จะ Redirect ไป Tab Configuration ของ Function แสดง API Gateway Trigger ที่เพิ่งสร้าง จากนั้นจด API Endpoint นี่คือ URL ที่จะเรียกจาก Browser พร้อม Form Data

กลับไปที่ HTML

สุดท้าย ทดสอบ Form ว่าส่งอีเมลได้จริงไหม

ปรับ JavaScript ให้จัดการส่ง Request ตอน Submit Form

const form = document.querySelector("form"); form.addEventListener("submit", (event) => { // prevent the form submit from refreshing the page event.preventDefault(); const { name, email, message } = event.target; // Use your API endpoint URL you copied from the previous step const endpoint = "<https://5ntvcwwmec.execute-api.us-east-1.amazonaws.com/default/sendContactEmail>"; // We use JSON.stringify here so the data can be sent as a string via HTTP const body = JSON.stringify({ senderName: name.value, senderEmail: email.value, message: message.value }); const requestOptions = { method: "POST", body }; fetch(endpoint, requestOptions) .then((response) => { if (!response.ok) throw new Error("Error in fetch"); return response.json(); }) .then((response) => { document.getElementById("result-text").innerText = "Email sent successfully!"; }) .catch((error) => { document.getElementById("result-text").innerText = "An unkown error occured."; }); }); 

ช่วงสำคัญ: กรอก Form กด Submit หากเห็น Success Message แปลว่าอีเมลส่งสำเร็จ

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

เพราะผมเป็นเจ้าของอีเมลที่รับ ผมเช็ค Inbox ดู ได้อีเมลจากตัวเองพร้อมรายละเอียดที่กรอกใน Form!

รับอีเมลจาก Contact Form อย่างปลอดภัยด้วย AWS SES, Lambda และ API Gateway

หากติดตามมาถึงที่นี่ คุณมี Contact Us Form ที่ทำงานจริง นำไปเสียบในเว็บไซต์ไหนก็ได้ และคุณจ่ายเงินเฉพาะตอนใช้งานจริง

ไม่รู้คุณแต่ผมคิดว่านี่ เก่งมากและเกือบ Magical! และเป็น Practical Way ใช้ Cloud Computing/Services ใน Workflow

แน่นอน Customize Flow ได้ ใช้ Framework Frontend อย่าง React/Vue หรือ Language อื่นสำหรับ Lambda อย่าง Python/Go

Before you go...

ขอบคุณที่อ่านจนจบ! ผมเขียนโพสต์เกี่ยว JavaScript, Cloud Development และประสบการณ์การศึกษา/งาน بصف Self-taught Developer ติดตามได้ที่ Twitter @adham_benhawy

Resources

  • https://aws.amazon.com/premiumsupport/knowledge-center/lambda-send-email-ses/
  • https://docs.aws.amazon.com/lambda/latest/dg/lambda-invocation.html
  • https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html?icmpid=docs_lambda_console