Xin chào tất cả các bạn, mình là Quân, hôm nay chúng ta sẽ cùng nhau đi triển khai một tính năng rất phổ biến mà hầu như ứng dụng web lớn hay nhỏ nào cũng cần, đó là gửi email nhé. Và công nghệ mình sử dụng và hướng dẫn cho các bạn vẫn là NodeJS thân thuộc 😀
“Bài này nằm trong loạt bài Lập Trình Nodejs từ cơ bản đến nâng cao trên trang blog chính thức trungquandev.com“
Những nội dung có trong bài:
- Giải thích về giao thức chuẩn truyền tải thư điện tử SMTP là gì?
- Dán mắt vào màn hình để code
- Demo ứng dụng gửi email với nodejs
- Full source code trên Github
Lưu ý quan trọng & Update:
Kể từ ngày 30-5-2022 Google sẽ không cho phép chúng ta sử dụng tính năng “Bật chế độ ứng dụng kém an toàn” cho việc xác thực tài khoản gửi mail như trong bài này mà mình đã hướng dẫn nữa (hình bên dưới)
– Giải pháp thay thế cũng như tốt hơn đó là sử dụng OAuth2 để xác thực & ủy quyền ứng dụng. Mình đã viết một bài hướng dẫn cập nhật cách làm mới ở đây, các bạn nên xem bài viết mới này nha:
– Link bài viết mới: NodeJS viết API gửi Email với OAuth2 và Nodemailer
- Dưới đây là Lý thuyết về SMTP và nội dung cách làm cũ!
1. Giải thích về giao thức chuẩn truyền tải thư điện tử SMTP là gì?
“SMTP viết tắt của Simple Mail Transfer Protocol dịch ra có nghĩa là giao thức truyền tải thư tín đơn giản hóa. Và giao thức này thực hiện nhiệm vụ chính là gửi mail còn việc nhận mail hay truy xuất dữ liệu mail server sẽ có giao thức IMAP hay POP3 đảm nhiệm sau đó.”
Tổng quan về cách mà SMTP hoạt động:
- Khi có một email cần được gửi đi, hệ thống SMTP sẽ tự động dựa vào tên địa chỉ email đó và chuyển thông báo tới máy chủ SMTP.
- Khi máy chủ SMTP nhận được tín hiệu, nó sẽ trao đổi giữa máy chủ SMTP và máy chủ DNS để tìm ra tên miền gốc tại Hostname trong máy chủ SMTP.
- Sau đó, máy chủ thực hiện kiểm tra sự trùng khớp trong thông tin người dùng và thông tin email rồi dựa vào kết quả đó để cho phép gửi nhận dữ liệu.
Trên kia mình chỉ tìm hiểu tổng quan và viết lại thật ngắn gọn cho các bạn có một cái nhìn bao quát về SMTP thôi chứ không đi sâu thêm nữa, bởi vì đi sâu vào nó thì còn rất rất nhiều kiến thức chuyên môn. Mà chúng ta, hoặc nói đúng hơn thì là mình, đứng trên vị trí của một lập trình viên phát triển ứng dụng cuối, không phải là một người chuyên đi cấu hình mạng hay server thì nắm được tổng quan là đủ rồi, “hãy nên đầu tư thời gian hợp lý vào việc mà bạn làm tốt nhất.”
Tiếp theo, chúng ta sẽ đi vào phần quan trọng nhất của bài hôm nay, đó là cắm mặt vào màn hình để code nhé =))
(Ngoài lề: Mấy bạn admin của mấy trang TopDev, TechBlog…vv gì đấy đã đi copy bài không phải của các bạn thì hãy tôn trọng người viết, đừng có xóa những liên kết (link) trong bài của mình như link ở trên cũng như tự ý xóa linh tinh các câu thoại của mình trong bài viết, từ giờ nếu mình phát hiện ra nữa thì chắc chắn sẽ ăn report DMCA nhé.)
2. Dán mắt vào màn hình để code
Cấu trúc thư mục dự án của chúng ta sẽ trông như sau:
src
controllers
emailController.js
homeController.js
utils
mailer.js
routes
web.js
views
master.html
server.js
package.json
Việc khởi tạo ứng dụng nodejs thì mình không làm lại, các bạn có thể xem cách làm ở các bài viết trước của mình tại link bên dưới đây:
https://trungquandev.com/series-lap-trinh-nodejs/
Tiếp theo, trong ví dụ ngày hôm nay, chúng ta sẽ cài đặt 2 module là:
express và nodemailer
npm install --save express nodemailer
Mình sẽ hướng dẫn lần lượt các file code như sau:
File src/views/master.html
File này các bạn cần lưu ý một vài chỗ:
- Thứ nhất là cái form gửi email
<form action="/send-email" method="post">
- Thứ 2 là các cái thẻ bên trong có name lần lượt là
name="to"
name="subject"
vàname="body"
để mình nhập thông tin gửi email - Cuối cùng là đoạn script
ClassicEditor.create...
ở phía cuối file là mình dùng thư viện ckeditor để dễ dàng trong việc soạn thảo nội dung cho email.
<!-- * Created by trungquandev.com's author on 18/02/2020. */ // views/master.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link rel="shortcut icon" href="https://trungquandev.com/wp-content/uploads/2016/11/LOGO.png" /> <title>NodeJS send email with nodemailer</title> <!-- Get bootstrap from CDN--> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> <style> .ck-content { height: 300px; } </style> </head> <body> <div class="container"> <h4 class="text-left mt-5"> NodeJS send email example - <a href="https://trungquandev.com" target="blank">trungquandev.com</a> </h4> <hr> <div class="text-left"> <form action="/send-email" method="post"> <div class="row"> <div class="form-group col-md-8"> <label for="to">To:</label> <input type="email" class="form-control" name="to" required> </div> </div> <div class="row"> <div class="form-group col-md-8"> <label for="subject">Subject:</label> <input type="text" class="form-control" name="subject" required> </div> </div> <div class="row"> <div class="form-group col-md-8"> <label for="body">Body:</label> <textarea id="classic-editor" cols="5" rows="5"class="form-control" name="body"></textarea> </div> </div> <div class="row"> <div class="form-group col-md-8"> <button type="submit" class="btn btn-success">Send</button> </div> </div> </form> </div> </div> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script src="https://cdn.ckeditor.com/ckeditor5/16.0.0/classic/ckeditor.js"></script> <script> $(document).ready(function() { ClassicEditor .create(document.querySelector('#classic-editor'), { initialData: ` <p>Cảm ơn các bạn đã ủng hộ xem bài viết trên trang blog <strong>trungquandev.com</strong> chính thức của mình.</p> <p>Warm regards, <a href="https://trungquandev.com">https://trungquandev.com</a></p> ` }) .catch(error => { console.error( error ) }) }) </script> </body> </html>
Đây là hình ảnh trực quan của cái form mà mình làm ở trên, mình gửi cho các bạn xem trước nhé, lát nữa làm xong hết file code rồi demo ứng dụng các bạn sẽ nắm được rõ hơn.
File src/utils/mailer.js
Trong file mailer.js này mình sẽ sử dụng thư viện nodemailer để khởi tạo một thằng transporter với những cấu hình cụ thể trong function sendMail và sau đó sử dụng nó để gửi email đến điểm cuối mà chúng ta mong muốn.
Lưu ý các bạn cần thay thế đúng địa chỉ và mật khẩu tài khoản email mà bạn sử dụng để gửi mail nhé. Nó nằm ở 2 biến cấu hình adminEmail
và adminPassword
ở bên dưới. (Mình sẽ dùng tài khoản gmail của google trong phần demo phía sau.)
File mailer.js này lát nữa chúng ta sẽ sử dụng ở emailController.js sau khi nhận được request gửi email từ form phía client.
/** * Created by trungquandev.com's author on 18/02/2020. * utils/mailer.js */ const nodeMailer = require('nodemailer') // Những thông tin dưới đây các bạn có thể ném nó vào biến môi trường env nhé. // Vì để demo nên mình để các biến const ở đây. const adminEmail = 'Thay thế chuỗi string này thành địa chỉ admin email của bạn.' const adminPassword = 'Thay thế chuỗi string này thành mật khẩu admin email của bạn.' // Mình sử dụng host của google - gmail const mailHost = 'smtp.gmail.com' // 587 là một cổng tiêu chuẩn và phổ biến trong giao thức SMTP const mailPort = 587 const sendMail = (to, subject, htmlContent) => { // Khởi tạo một thằng transporter object sử dụng chuẩn giao thức truyền tải SMTP với các thông tin cấu hình ở trên. const transporter = nodeMailer.createTransport({ host: mailHost, port: mailPort, secure: false, // nếu các bạn dùng port 465 (smtps) thì để true, còn lại hãy để false cho tất cả các port khác auth: { user: adminEmail, pass: adminPassword } }) const options = { from: adminEmail, // địa chỉ admin email bạn dùng để gửi to: to, // địa chỉ gửi đến subject: subject, // Tiêu đề của mail html: htmlContent // Phần nội dung mail mình sẽ dùng html thay vì thuần văn bản thông thường. } // hàm transporter.sendMail() này sẽ trả về cho chúng ta một Promise return transporter.sendMail(options) } module.exports = { sendMail: sendMail }
File src/controllers/homeController.js
File này không có gì khó hiểu cả, trong nó có một function getHome trả về cái màn hình cho chúng ta nhập thông tin cho email, chính là sử dụng module path để lấy ra file master.html ở trên kia:
/** * Created by trungquandev.com's author on 18/02/2020. * homeController.js */ const path = require('path') let getHome = (req, res) => { return res.sendFile(path.join(`${__dirname}/../views/master.html`)) } module.exports = { getHome: getHome }
File src/controllers/emailController.js
Trong emailController này chúng ta sẽ import module mailer vừa viết ở trên kia, sau đó kết hợp cùng với những thông tin request từ phía client trong req.body, cuối cùng là gọi hành động gửi email. Nếu có lỗi hoặc thành công trong quá trình thì đều sẽ gửi về cho client biết.
/** * Created by trungquandev.com's author on 18/02/2020. * emailController.js */ const mailer = require('../utils/mailer') let sendMail = async (req, res) => { try { // Lấy data truyền lên từ form phía client const { to, subject, body } = req.body // Thực hiện gửi email await mailer.sendMail(to, subject, body) // Quá trình gửi email thành công thì gửi về thông báo success cho người dùng res.send('<h3>Your email has been sent successfully.</h3>') } catch (error) { // Nếu có lỗi thì log ra để kiểm tra và cũng gửi về client console.log(error) res.send(error) } } module.exports = { sendMail: sendMail }
File src/routes/web.js
File này là nơi chúng ta cấu hình routers cho ứng dụng, sẽ có 2 routes chính, một là để gọi ra màn hình nhập thông tin email để gửi đi, có method là GET, route còn lại là gọi đến emailController ở ngay phía trên để thực hiện hành động gửi email, method là POST.
/** * Created by trungquandev.com's author on 18/02/2020. * routes/web.js */ const express = require('express') const router = express.Router() const homeController = require('../controllers/homeController') const emailController = require('../controllers/emailController') let initRoutes = (app) => { // Gọi ra trang home cho việc upload router.get('/', homeController.getHome) // Gọi hành động gửi email router.post('/send-email', emailController.sendMail) return app.use('/', router) }; module.exports = initRoutes
File src/server.js
Và cuối cùng, “last but not least”, file server.js của chúng ta, sử dụng để chạy ứng dụng nodejs:
/** * Created by trungquandev.com's author on 18/02/2020. * server.js */ const express = require('express') const app = express() const initRoutes = require('./routes/web') // Cho phép lý dữ liệu từ form method POST app.use(express.urlencoded({extended: true})) // Khởi tạo các routes cho ứng dụng initRoutes(app) // chọn một port mà bạn muốn và sử dụng để chạy ứng dụng tại local const port = 8017 app.listen(port, () => { console.log(`Hello trungquandev.com, I'm running at localhost:${port}/`) })
3. Demo ứng dụng gửi email với nodejs
Sau khi đã xử lý xong tất cả các file ở trên, chúng ta sẽ chạy ứng dụng bằng lệnh:node src/server.js
Kết quả đầu tiên sẽ hiện lên màn hình cho chúng ta nhập thông tin email gửi đi, mình sẽ nhập vài thông tin như hình bên dưới:
Nếu không có vấn đề gì xảy ra, thì kết quả email mà mình nhận được sẽ trông như thế này:
Nhưng mọi chuyện đâu có đơn giản như thế =)) Trong quá trình làm demo, mình có dùng account gmail để làm tài khoản admin gửi email, nhưng bảo mật của google rất cao, nên chắc chắn các bạn sẽ bị gặp lỗi này:
{ Error: Invalid login: 535-5.7.8 Username and Password not accepted. Learn more at …vv }
Và ngoài ra thì còn được bonus thêm một email cảnh báo rất chuyên nghiệp từ google =))
Sau đây mình sẽ hướng dẫn cách fix cho các bạn:
Các bạn truy cập vào phần Google tài khoản > chọn mục Bảo Mật
Kéo xuống dưới, các bạn sẽ thấy phần “Quyền truy cập của ứng dụng kém an toàn” mặc định là đang tắt, hãy bật nó lên nhé.
Sau khi xong thì quay ra chạy demo lại ngon rồi đấy, nếu gặp lỗi gì khác, thì hãy comment bên dưới bài viết này mình sẽ kiểm tra nhé.
4. Full source code trên Github
Vậy là kết thúc bài hôm nay chúng ta đã cùng nhau hoàn thiện về ý tưởng và cách triển khai cho việc gửi nhận email trong nodejs rồi.
Mình có để full source code của bài hôm nay ở repo này cho các bạn tham khảo nhé, nếu thấy bài viết bổ ích, hãy ủng hộ bằng cách cho mình 1 star trên repo này để mình có động lực tiếp tục viết những bài viết chất lượng nha, cảm ơn các bạn.
https://github.com/trungquandev/nodejs-send-email
Cảm ơn các bạn đã dành thời gian đọc bài viết.
Xin chào và hẹn gặp lại các bạn ở những bài viết tiếp theo.
Best Regards – Trung Quân – Green Cat
Tham khảo kiến thức:
“Thanks for awesome knowledges.”