Gửi Email trong ứng dụng Rails
Giới thiệu Ở bài viết này chúng ta sẽ làm một ứng dụng đơn giản để trình diễn việc gửi email với ActionMailer, ActionMailer Preview và thông qua một nhà cung cấp dịch vụ bên thứ ba như Gmail hoặc Mailgun. Chúng ta sẽ demo cách sử dụng Active job để gửi email cùng với background processor (tiến ...
Giới thiệu
Ở bài viết này chúng ta sẽ làm một ứng dụng đơn giản để trình diễn việc gửi email với ActionMailer, ActionMailer Preview và thông qua một nhà cung cấp dịch vụ bên thứ ba như Gmail hoặc Mailgun. Chúng ta sẽ demo cách sử dụng Active job để gửi email cùng với background processor (tiến trình chạy ngầm).
Bạn có thể tìm thấy code của tutorial tại đây
Gửi mail sử dụng ActionMailer và Gmail
Bây giờ chúng ta sẽ xây dựng một ứng dụng rails để gửi email đến user khi một user mới được tạo
$ rails new new_app_name $ rails g scaffold user name:string email:string $ rake db:migrate
Bây giờ chúng ta đã có một ứng dụng cơ bản, chúng ta sẽ sử dụng ActionMailer. Việc tạo mới Mailer cũng tương tự như bất kỳ tạo mới nào trên rails.
$ rails g mailer example_mailer create app/mailers/example_mailer.rb invoke erb create app/views/example_mailer invoke test_unit create test/mailers/example_mailer_test.rb create test/mailers/previews/example_mailer_preview.rb
Ứng dụng mà chúng ta đang sử dụng là bản Rails 4.2.0.beta4 do đó khi tạo mailer thì ứng dụng có tự động tạo ra một file test là example_mailer_preview.rb đặt trong thư mục previews, để sau này có thể được sử dụng
Trong file app/mailers/example_mailer.rb
class ExampleMailer < ActionMailer::Base default from: "from@example.com" end
Bây giờ chúng ta sẽ viết ra một phương thức và customized email. Trước tiên, bạn nên thay đổi địa chỉ gửi mail mặc định từ from@example.com thành địa chỉ mail mà bạn muốn gửi đi (hay còn gọi là sender’s address).
class ExampleMailer < ActionMailer::Base default from: "from@example.com" def sample_email user @user = user mail to: @user.email, subject: "Sample Email" end end
Phương thức sample_email lấy các parameter của user và gửi email sử dụng phương thức mail để gửi đến địa chỉ email của user. Trong trường hợp bạn muốn biết thêm về các tính năng như tập tin đính kèm hay là gửi cho nhiều email, bạn có thể xem hướng dẫn trong tài liệu tham khảo ở link bên dưới. Bây giờ chúng ta sẽ viết nội dung mail mà bạn muốn gửi đến cho user, tất cả đều được lưu trong thư mục app/views/example_mailer. Bạn tạo ra một file tên là sample_email.html.erb có định dạng chuẩn HTML
Trong file app/views/example_mailer/sample_email.html.erb
<!DOCTYPE html> <html> <head> <meta content='text/html; charset=UTF-8' http-equiv='Content-Type' /> </head> <body> <h1>Hi <%= @user.name %></h1> <p> Sample mail sent using smtp. </p> </body> </html>
Nếu chúng ta cần tạo ra dạng văn bản(cách dòng hoặc thụt vào đầu dòng) cho email thì bạn phải tạo ra file sample_email.text.erb thay cho file sample_email.html.erb trong thư mục app/views/example_mailer
Trong file app/views/example_mailer/sample_email.text.erb
Hi <%= @user.name %> Sample mail sent using smtp.
Trong môi trường test, chúng ta sử dụng ActionMailer Preview để test ứng dụng của chúng ta. Chúng ta sẽ sử dụng test/mailers/previews/example_mailer_preview.rb để tạo ra các mailer. Chúng ta có thể gọi đến bất kỳ user nào (trong trường hợp này chỉ gọi đến user đầu tiên) để test các email.
# Test tất cả các email tại http://localhost:3000/rails/mailers/example_mailer class ExampleMailerPreview < ActionMailer::Preview def sample_mail_preview ExampleMailer.sample_email(User.first) end end
Khi bạn truy cập địa chỉ http://localhost:3000/rails/mailers/example_mailer/sample_mail_preview bạn sẽ nhìn thấy preview của email. Theo mặc định email previews sẽ được đặt trong test/mailers/previews. Bạn có thể thay đổi điều này bằng cách thiết lập một đường dẫn khác trong /config/environments/development.rb. Chỉ cần thiết lập config.action_mailer.preview_path đến đường dẫn mong muốn và thêm file preview vào vị trí tương ứng.
Gửi mail sử dụng ActionMailer và Gmail
Theo mặc định rails sẽ gửi email thông qua SMTP. Do đó chúng ta sẽ cấu hình SMTP trong cài đặ môi trường /config/environments/production.rb. Đầu tiên chúng ta sẽ nhìn thấy cấu hình này là để chạy gửi email của Gmail. Trước khi chúng ta lưu các thông tin nhạy cảm này như username và password trong biến mội trường. Chúng ta cần sử dụng gem figaro. Để biết thêm thông tin chi tiết làm thế nào để quản lý biến môi trường bạn đọc trong tài liệu tại đây
Trong file /config/application.yml
gmail_username: 'username@gmail.com' gmail_password: 'Gmail password'
Trong file /config/environments/production.rb
config.action_mailer.delivery_method = :smtp # SMTP settings for gmail config.action_mailer.smtp_settings = { :address => "smtp.gmail.com", :port => 587, :user_name => ENV['gmail_username'], :password => ENV['gmail_password'], :authentication => "plain", :enable_starttls_auto => true }
Bây giờ chúng ta sẽ chỉnh sửa UsersController để gọi đến sự kiện gửi email cho user. Chúng ta cần thêm vào ExampleMailer.sample_email(@user).deliver cho phương thức tạo mới trong app/controllers/users_controller.rb. Phương thức tạo mới trong users_controller.rb như sau:
def create @user = User.new(user_params) respond_to do |format| if @user.save # Sends email to user when user is created. ExampleMailer.sample_email(@user).deliver format.html { redirect_to @user, notice: 'User was successfully created.' } format.json { render :show, status: :created, location: @user } else format.html { render :new } format.json { render json: @user.errors, status: :unprocessable_entity } end end end
Khi một user mới được tạo ra, chúng ta sẽ gửi một email thông qua phương thức sample_email trong mailer ExampleMailer.
Gửi email cùng với Background Processor thông qua Active Job
Khi bạn kiểm tra các ứng dụng, bạn sẽ thấy rằng sẽ mất rất nhiều thời gian (lâu hơn so với bình thường) để tạo ra một user mới. Chuyện này xảy ra bởi vì chúng ta phải gọi đến một API bên ngoài để gửi email. Việc này sẽ trở thành vấn đề lớn nếu cùng một lúc bạn gửi đi nhiều email hoặc gửi email đến nhiều người dùng. Vấn đề này sẽ được dễ dàng giải quyết nếu bạn bỏ các email gửi đi vào background jobs. Trong ứng dụng của chúng ta, chúng ta sẽ sử dụng Active Jobs và Delayed Job để gửi những email trong background.
Active Jobs là một bộ chuyển đổi cung cấp giao diện tổng quát cho các xử lý background như Resque, Delayed Job, Sidekiq,..vv. Chú ý rằng sử dụng Active Job bạn sẽ cần phiên bản Rails 4.2 trở lên.
$ rails g job send_email invoke test_unit create test/jobs/send_email_job_test.rb create app/jobs/send_email_job.rb
Bây giờ chúng ta sẽ viết ra job để thực hiện bởi workers. Active Job được tích hợp với ActionMailer do đó dễ dàng để gửi các email bất đồng bộ. Đối với việc gửi email qua Active Job chúng ta sử dụng phương thức deliver_later.
Trong file /app/jobs/send_email_job.rb
class SendEmailJob < ActiveJob::Base queue_as :default def perform user @user = user ExampleMailer.sample_email(@user).deliver_later end end
Bây giờ chúng ta sẽ sửa đổi trong phương thức create khi tạo mới user. Thay vì việc gửi email luôn khi tạo user, chúng ta sẽ đưa email vào hàng đợi(enqueue) và gọi đến hàm perform sau đó.
Trong file app/controllers/users_controller.rb
def create ... SendEmailJob.set(wait: 20.seconds).perform_later(@user) .... end
Bây giờ chúng ta cần cấu hình backend cho tiến trình background của chúng ta. Chúng ta lựa chọn delayed_jobs để quản lý backend nhưng bạn cũng có thể lựa chọn backend của bạn nếu bạn muốn. Active Job có nhiều built-in adapter cho nhiều hàng đợi của backend.
Trong file Gemfile
gem 'delayed_job_active_record'
$ bundle $ rails generate delayed_job:active_record $ rake db:migrate
Thiết lập hàng đợi backend cho môi trường production
Trong file /config/environments/production.rb
config.active_job.queue_adapter = :delayed_job
Bây giờ tất cả mọi thứ đã được cấu hình, để test cho ứng dụng bạn vào tạo mới user. Một job mới sẽ được thêm vào hàng đợi và bạn sẽ nhận thấy thời gian cần thiết để tạo mới một user đã giảm đáng kể. Bạn bắt đầu chạy các job trong hàng đợi bằng lệnh sau:
$ bundle exec rake jobs:work